Annotation工作方式: 从java5.0以来,提供了一个真实的annotation功能:允许开发者定义、使用自己的额annotation类型,此功能由一个定义annotation类型的语法和一个描述annotation声明的语法,读取annotation的API,一个使用annotation修饰的Class文件,一个annotation处理工具apt组成。
annotation并不会直接影响代码语义 ,但是它能够工作的方式被看做类似程序的工具或者类库,他会反过来对正在运行的程序语义有所影响。annotation可以从源文件,class文件或者以在运行时反射的多种方式被读取。
注解可以用在方法,类,变量等上面,注解的使用范围很广。
Override注解表示子类要重写(override)父类方法
1 2 3 4 5 6 7 8 9 10 11 12 public class OverrideTest { @Override public String toString() { return "This is OverrideTest"; } public static void main(String[] args) { OverrideTest overrideTest = new OverrideTest(); System.out.println(overrideTest); } }
Deprecate注解表示方法是不建议被使用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.Date; public class DeprecatedTest { @Deprecated public void doSomething() { System.out.println("do something "); } public static void main(String[] args) { DeprecatedTest deprecatedTest= new DeprecatedTest(); //不建议被使用的 deprecatedTest.doSomething(); // Date d= new Date(); // System.out.println(d.toLocaleString()); } }
SuppressWarnings注解表示抑制警告。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.util.Date; import java.util.Map; import java.util.TreeMap; public class SuppressWarningsTest { @SuppressWarnings("unchecked")//不检查 public static void main(String[] args) { Map map=new TreeMap(); map.put("hello", new Date()); System.out.println("hello"); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.Date; import java.util.Map; import java.util.TreeMap; public class SuppressWarningsTest { @SuppressWarnings({"unchecked","deprecation"})//不检查,不推荐使用的 public static void main(String[] args) { Map map=new TreeMap(); map.put("hello", new Date()); System.out.println("hello"); Date date=new Date(); System.out.println(date.toLocaleString()); } }
注解跟着值的时候可以使用大括号,也可以省略。@SuppressWarnings({“unchecked”,”deprecation”})
以上就是注解的使用方式,我们也可以自己定义注解,下面来介绍一下自定义注解。
当注解中的属性名为value时,在对其赋值时可以不指定属性的名称而直接写上属性值即可,除了value以外的其他值都需要使用name=value这种赋值方式,即明确指定给谁赋值。
1 2 3 4 5 6 7 public @interface AnnotationTest { //定义属性 String value(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @AnnotationTest(value="hello") public class AnnoationUsage { @AnnotationTest(value="world") public void method() { System.out.println("usage of annoation"); } public static void main(String[] args) { AnnoationUsage annoationUsage = new AnnoationUsage(); annoationUsage.method(); } }
注解的值可以默认的具体赋值,使用@interface自行定义Annotation形态时,实际上是自动继承了java.lang.annotation.Annotation接口由编译程序自动为您完成其他产生的细节,在定义Annotation形态时,不能继承其他的Annotation形态或是接口。
当我们使用@interface关键字定义一个注解时,该注解隐含地继承了java.lang.annotation.Annotation接口;如果我们定义了一个接口,并且让该接口继承自Annotation,那么我们所定义的接口依然还是接口而不是注解;Annotation本身是接口而不是注解。可以与Enum类比。
定义annotation类型时也可以使用包来管理类别,方式同于类的导入功能
告知编译器如何处理@Retention 使用java.lang.annotation.Retention,在定义时要指定java.lang.annotation.RetentionPolicy的枚举值之一
java.lang.annotation.Retention形态可以在你定义Annotation时,指示编译程序该如何对待您的自定义的Annotation。预设上编译程序会将Annotation信息留在.class档案中,但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息。
Retention和RetentionPolicy总是成对出现的。1 2 3 4 5 6 public enum RetentionPolicy { SOURCE,//编译程序处理完annotation信息后就完成任务 CLASS,//编译程序将annotation储存于Class档中,默认就是这个 RUNTIME//编译程序将annotation储存于Class文档中,可以由VM读入 }
为SOURSE的例子是 @SuppressWarnings仅在编译时期告知编译程序来抑制警告,所以不必将这个信息储存于class文档 为RUNTIME的时机,可以像是你使用java设计一个程序代码分析工具,你必须让vm能读出Annotation信息,以便在分析程序时使用,搭配反射机制就可以达到这个目的
定义annotation时必须设定RetentionPolicy为RUNTIME,也就是可以在vm中读取Annotation信息
1 2 3 4 5 6 7 8 9 10 import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; //自定义一个注解 @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String hello() default "zhangsan"; String world(); }
1 2 3 4 5 6 7 8 9 10 11 @MyAnnotation(hello="beijing",world="shanghai") public class MyTest { //一个方法可以由多个注解修饰 @MyAnnotation(hello="tianjin",world="shangdi") @Deprecated @SuppressWarnings("unchecked") public void output() { System.out.println("output method"); } }
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 import java.lang.reflect.Method; public class MyReflaction { public static void main(String[] args)throws Exception { MyTest myTest = new MyTest(); Class<MyTest> class1= MyTest.class; Method method =class1.getMethod("output", new Class[] {}); //判断是否存在这样也一个Annotation if(method.isAnnotationPresent(MyAnnotation.class)) { method.invoke(myTest, new Object[] {}); //得到一个Annotation,返回这个Annotation的一个实例 MyAnnotation myAnnotation =method.getAnnotation(MyAnnotation.class); //获得这个Annotation的属性值 String hello=myAnnotation.hello(); String world=myAnnotation.world(); System.out.println(hello+" ,"+world); } //输出结果完全是由RetentionPolicy决定的,只有RUNTIME的可以读取到 Annotation[] annotations=method.getAnnotations(); //必须RetentionPolicy为RUNTIME才可以读取到 for(Annotation annotation: annotations) { System.out.println(annotation.annotationType().getName()); } } }
运行结果:
1 2 3 4 output method tianjin ,shangdi MyAnnotation java.lang.Deprecated
这个例子主要练习了几个和反射、注解有关的方法。只要理解了反射机制就很容易理解了,我再注释中都已经详细的介绍了,这里就不过多的描述了。
限定annotation使用对象@Target 使用java.lang.annotation.Target可以定义其使用之时机,在定义时要指定java.lang.annotation.ElementType的枚举值之一
1 2 3 4 5 6 7 8 9 10 11 12 13 package java.lang.annotation public enum ElementType { TYPE,//适用Class,interface ,enum FIELD,//适用文件 METHOD,//适用于方法 PARAMETER,//适用method上之parameter CONSTRUCTOR,//适用constructor LOCAL_VARIABLE,//适用局部变量 ANNOTATION_TYPE,//适用annotation形态 PACKAGE//适用package }
要求为API文件@Documented 想要在使用者制作javaDoc文件的同时,也一并将Annotation的信息加入至API文档中使用。
子类是否继承父类@Inherited 预设上父类中的annotation并不会被继承至子类中,可以在定义Annotation形态时加上java.lang.annotation.Inherited形态的Annotation。
注解在实际中的应用,注解主要应用在单元测试中,我来简单的说一下。
Junit有两个经典的版本,Junit3和Junit4。
Junit3继承TestCase,方法必须以test开头。keep the bar green to keep the code clean.这些底层都是通过反射机制实现的。
Junit3是基于反射的,Junit4是基于反射和注解的
JUnit4的执行的一般流程:
首先获得待测试类所对应的Class对象。
然后通过该Class对象获得当前类中所有public方法所对应的Method数组。
遍历该Method数组,取得每一个Method对象
调用每个Method对象的isAnnotationPresent(Test.class)方法,判断该方法是否被Test注解所修饰。
如果该方法返回true,那么调用method.invoke()方法去执行该方法,否则不执行。
我的csdn博客