Java

概念

概念:说明程序的。给计算机看的
注释:用文字描述程序的。给程序员看的
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

作用

作用分类:

  • 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
  • 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
  • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

    JDK中预定义的一些注解

  • **@Override**:检测被该注解标注的方法是否是继承自父类(接口)的

  • **@Deprecated**:该注解标注的内容,表示已过时
  • **@SuppressWarnings**:压制警告,一般传递参数all **@SuppressWarnings("all")**

    注解生成文档案例

    要生成doc文档的api类案例:

    1. /**
    2. * 注解javadoc演示
    3. *
    4. * @version 1.0
    5. * @since 1.5
    6. */
    7. public class AnnoDoc {
    8. /**
    9. * 计算两数的和
    10. * @param a 整数
    11. * @param b 整数
    12. * @return 两数的和
    13. */
    14. public int add(int a, int b ){
    15. return a + b;
    16. }
    17. }

    在对应的类目录进入cmd,执行如下命令

    1. javadoc AnnoDoc.java

    执行完成后可以看到生成了很多html和js等前端文件,点击index.html。
    可以看到如下效果:
    自定义注解 - 图1
    自定义注解 - 图2

    自定义注解

    格式

    元注解

    1. public @interface 注解名称{
    2. 属性列表;
    3. }

    本质

    注解本质上就是一个接口,该接口默认继承Annotation接口

    1. public interface MyAnno extends java.lang.annotation.Annotation {}

    属性:接口中的抽象方法

    要求:

    1、属性的返回值类型有下列取值

  • 基本数据类型

  • String
  • 枚举
  • 注解

以上类型的数组
2、定义了属性,在使用时需要给属性赋值

  • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
  • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
  • 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
    案例:
    定义: ```java public @interface MyAnno { int value(); Person per(); MyAnno2 anno2(); String[] strs(); }

public enum Person {

  1. P1,P2;

}

  1. 使用:
  2. ```java
  3. @MyAnno(value=12,per = Person.P1,anno2 = @MyAnno2,strs="bbb")
  4. public class Worker {
  5. }

元注解:用于描述注解的注解

  • **@Target**:描述注解能够作用的位置
    • **TYPE**:可以作用于类上
    • **METHOD**:可以作用于方法上
    • **FIELD**:可以作用于成员变量上
    • **ElementType**取值:
  • **@Retention**:描述注解被保留的阶段
    • **@Retention(RetentionPolicy.RUNTIME)**:当前被描述的注解,会保留到class字节码文件中,并被JVM读取到,自定义注解一般用这个。
  • **@Documented**:描述注解是否被抽取到api文档中
  • **@Inherited**:描述注解是否被子类继承

    在程序使用(解析)注解:获取注解中定义的属性值

    之前在反射中有通过读取配置文件来创建任意类的对象,执行任意方法。通过读取对应的配置文件然后创建类和执行方法,代码如下: ```java /* 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法 /

//1.加载配置文件 //1.1创建Properties对象 Properties pro = new Properties(); //1.2加载配置文件,转换为一个集合 //1.2.1获取class目录下的配置文件 ClassLoader classLoader = ReflectTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream(“pro.properties”); pro.load(is);

//2.获取配置文件中定义的数据 String className = pro.getProperty(“className”); String methodName = pro.getProperty(“methodName”);

//3.加载该类进内存 Class cls = Class.forName(className); //4.创建对象 Object obj = cls.newInstance(); //5.获取方法对象 Method method = cls.getMethod(methodName); //6.执行方法 method.invoke(obj);

  1. 可以通过注解替换上述读取配置文件相关操作。具体代码如下:<br />注解定义如下:
  2. ```java
  3. /**
  4. * 描述需要执行的类名,和方法名
  5. */
  6. @Target({ElementType.TYPE})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Pro {
  9. String className();
  10. String methodName();
  11. }

通过解析注解配置,执行相关对象创建和执行对象方法。

  • 获取注解定义的位置的对象 (Class,Method,Field)
  • 获取指定的注解
  • 调用注解中的抽象方法获取配置的属性值

代码如下:

  1. @Pro(className = "com.zjq.javabase.base25.annotation.Demo1",methodName = "show")
  2. public class ReflectTest {
  3. public static void main(String[] args) throws Exception {
  4. /**
  5. * 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
  6. */
  7. //1.解析注解
  8. //1.1获取该类的字节码文件对象
  9. Class<ReflectTest> reflectTestClass = ReflectTest.class;
  10. //2.获取上边的注解对象
  11. //其实就是在内存中生成了一个该注解接口的子类实现对象
  12. /*
  13. public class ProImpl implements Pro{
  14. public String className(){
  15. return "com.zjq.javabase.base25.annotation.Demo1";
  16. }
  17. public String methodName(){
  18. return "show";
  19. }
  20. }
  21. */
  22. Pro an = reflectTestClass.getAnnotation(Pro.class);
  23. //3.调用注解对象中定义的抽象方法,获取返回值
  24. String className = an.className();
  25. String methodName = an.methodName();
  26. System.out.println(className);
  27. System.out.println(methodName);
  28. //4.加载该类进内存
  29. Class cls = Class.forName(className);
  30. //5.创建对象
  31. Object obj = cls.newInstance();
  32. //6.获取方法对象
  33. Method method = cls.getMethod(methodName);
  34. //7.执行方法
  35. method.invoke(obj);
  36. }
  37. }

案例:通过自定义注解定义一个简单的测试框架

定义一个测试注解

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. public @interface Check {
  4. }

定义一个计算器工具类,并在方法上使用@Check注解

  1. /**
  2. * 小明定义的计算器类
  3. */
  4. public class Calculator {
  5. //加法
  6. @Check
  7. public void add(){
  8. String str = null;
  9. str.toString();
  10. System.out.println("1 + 0 =" + (1 + 0));
  11. }
  12. //减法
  13. @Check
  14. public void sub(){
  15. System.out.println("1 - 0 =" + (1 - 0));
  16. }
  17. //乘法
  18. @Check
  19. public void mul(){
  20. System.out.println("1 * 0 =" + (1 * 0));
  21. }
  22. //除法
  23. @Check
  24. public void div(){
  25. System.out.println("1 / 0 =" + (1 / 0));
  26. }
  27. public void show(){
  28. System.out.println("永无bug...");
  29. }
  30. }

定义测试框架类并执行测试,把测试异常记录到bug.txt文件中,代码如下:

  1. /**
  2. * 简单的测试框架
  3. * 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法),判断方法是否有异常,
  4. * 记录到文件中
  5. *
  6. */
  7. public class TestCheck {
  8. public static void main(String[] args) throws IOException {
  9. //1.创建计算器对象
  10. Calculator c = new Calculator();
  11. //2.获取字节码文件对象
  12. Class cls = c.getClass();
  13. //3.获取所有方法
  14. Method[] methods = cls.getMethods();
  15. int number = 0;//出现异常的次数
  16. BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
  17. for (Method method : methods) {
  18. //4.判断方法上是否有Check注解
  19. if (method.isAnnotationPresent(Check.class)) {
  20. //5.有,执行
  21. try {
  22. method.invoke(c);
  23. } catch (Exception e) {
  24. //6.捕获异常
  25. //记录到文件中
  26. number++;
  27. bw.write(method.getName() + " 方法出异常了");
  28. bw.newLine();
  29. bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
  30. bw.newLine();
  31. bw.write("异常的原因:" + e.getCause().getMessage());
  32. bw.newLine();
  33. bw.write("--------------------------");
  34. bw.newLine();
  35. }
  36. }
  37. }
  38. bw.write("本次测试一共出现 " + number + " 次异常");
  39. bw.flush();
  40. bw.close();
  41. }
  42. }

执行测试后可以在src同级目录查看到bug.txt文件内容如下:

  1. add 方法出异常了
  2. 异常的名称:NullPointerException
  3. 异常的原因:null
  4. --------------------------
  5. div 方法出异常了
  6. 异常的名称:ArithmeticException
  7. 异常的原因:/ by zero
  8. --------------------------
  9. 本次测试一共出现 2 次异常

总结

大多数时候,只是使用注解,而不是自定义注解。
注解不是程序的一部分,可以理解为注解就是一个标签。
注解给谁用?

  • 编译器
  • 给解析程序用