注解
所谓注解,跟注释类似,用来对代码中的类、方法、属性进行说明。
作用
编写文档:通过代码里面标识的注解来生成文档javadoc命令
代码分析:可以通过反射机制,得知该类、方法、属性是否有注解。比如可以通过反射,调用getAnnotation函数获取该注解然后进行解析注解中的属性。
编译检查:通过注解能够让编译器自动进行编译检查,比如@overide可以检查是否是重载函数
可以暂时理解为一个可以存放数据(注解的属性)的“特殊对象”。他的作用主要体现在可以解析他的属性值。看完《自定义注解》就明白了。
元注解
元注解就是标识注解的注解。也就是系统中定义好的注解,方便我们自定义注解的时候对该自定义注解进行描述。
这里只介绍两种最常用的元注解
@Target
这个元注解表示被描述的注解@Anno的适用范围,也就是说@Anno可以作用于类(TYPE)还是属性(FIELD),又或是方法(METHOD)
@Retention
Retention中文维持,表示被描述的注解的保持时间。我们一般设置为RUNTIME,表示时间是从字节码到JVM读取的时间段都能保持。
自定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package cn.itcast.example.example01;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAno { String className(); String methodName(); }
|
定义注解的格式
- 通过@interface表示是一个注解(本质上是一个接口)
- 注解内部是属性列表,跟定义成员方法一样,返回值+函数名。
- 注解上面是元注解,用来限定该注解的特性,比如作用范围、保持时间等。
解析注解
解析注解就是读取注解的属性。具体步骤
- 创建字节码对象
- 字节码对象调用getAnnotation获取
案例1-通过注解获取类名和方法,并执行类中的方法
步骤
- 获取字节码对象
- 字节码对象调用getAnnotation获取注解对象,然后获取注解对象属性值类名和方法名
- 利用反射执行类中方法
代码
MyAno.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package cn.itcast.example.example01;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAno { String className(); String methodName(); }
|
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package cn.itcast.example.example01;
@MyAno(className = "cn.itcast.example.domain.Cat",methodName ="shout" ) public class Main { public static void main(String[] args) throws Exception { Class<Main> cls = Main.class; MyAno ano = cls.getAnnotation(MyAno.class); String cn = ano.className(); String mn = ano.methodName(); Class<?> cls1 = Class.forName(cn); Object o = cls1.getConstructor().newInstance(); cls1.getMethod(mn).invoke(o);
} }
|
Cat.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package cn.itcast.example.domain;
public class Cat { private String name;
public Cat() { }
public Cat(String name) {
this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } public void shout(){ System.out.println("喵喵喵"); } }
|
Dog.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package cn.itcast.example.domain;
public class Dog { private String name;
public Dog(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Dog() { } public void shout(){ System.out.println("哇哦哇哦"); } }
|
案例2-通过注解实现测试框架,测试Calculator中的各个方法
步骤
- 自定义一个方法注解
- 在需要测试的方法上加上注解
- 在测试类中利用反射机制获取Calculator字节码对象,获取该对象的所有方法对象。
- 方法对象调用getAnnotation获取注解,根据注解有无执行该方法对象。(主要利用了反射机制)
代码
Calculator.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package cn.itcast.example.example02;
public class Calculator { @Check public void add(){ String str = null; str.toString(); System.out.println("1 + 0 =" + (1 + 0)); } @Check public void sub(){ System.out.println("1 - 0 =" + (1 - 0)); } @Check public void mul(){ System.out.println("1 * 0 =" + (1 * 0)); } @Check public void div(){ System.out.println("1 / 0 =" + (1 / 0)); }
public void show(){ System.out.println("永无bug..."); } }
|
Check.java
1 2 3 4 5 6 7 8 9 10 11 12
| package cn.itcast.example.example02;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Check { }
|
TestCheck.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package cn.itcast.example.example02;
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class TestCheck { public static void main(String[] args) throws Exception{ Calculator calculator = new Calculator(); Class cls = calculator.getClass(); Method[] declaredMethods = cls.getDeclaredMethods();
BufferedWriter fw = new BufferedWriter(new FileWriter("bug.txt")); int sum=0,erorNum=0; for (Method declaredMethod : declaredMethods) {
Check annotation = declaredMethod.getAnnotation(Check.class); if(null!=annotation){ sum++; try{ declaredMethod.invoke(calculator); }catch (Exception e){ fw.write(declaredMethod.getName()+"出异常了"); fw.newLine(); fw.write(e.getCause().toString()); fw.newLine(); fw.write("---------------"); fw.newLine(); erorNum++; }
}
} fw.write("一共测试了"+sum+"个函数"); fw.newLine(); fw.write("出错了"+erorNum+"个函数"); fw.newLine(); fw.flush(); fw.close(); } }
|
总结
实际开发过程中我们一般是使用注解,一般不会去自定义注解,但是我们需要知道注解本质就是一个接口,在接口中定义一些方法列表(在注解中称为属性列表)。
可以利用反射机制获取注解,从而解析注解得到里面的属性。