注解:认识注解、注解的分类、注解的生命周期、自定义注解及反射调用、为注解添加属性
1 认识注解(Annotation)
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记。
以后,javac编译器、开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。
注解可以加在包,类,字段,方法,方法的参数以及局部变量上。
注解的存在不影响程序的编译和执行。
javac编译器看到注解,就会去做相应的动作。
注解是JDK1.5的新特性。
一个注解就是一个类,使用注解,就相当于创建了一个对象。
一些初级注解的应用,
@Deprecated、@SurppressWarnings、@Override:
1,javac编译器看到了@Deprecated,就会在eclipse的界面中被@Deprecated修饰的元素上打删除线,用于提示开发人员这种方法已经过时,不再被推荐使用。但是这并不影响源代码的编译和执行。真正执行的时候,程序该怎么做还是怎么做。
2,开发人员对过时的方法打上@SurppressWarnings注解,就是告诉编译器,虽然我用的方法过时了,但是我还是坚持要用,你就不要再提示了。但是,这仅仅提示开发人员这样做可能压制了潜在的问题,不会影响程序的编译和执行。
3,有时候我们写的某些方法需要覆盖父类的方法,但是可能方法名或者参数会出现不小心写错的情况。这时候就可以为这个方法打上@Override注解,如果有任何差错,eclipse就会报错。
2 注解的分类
注解按照使用的方式和用途,注解可以分为三大类。
(1)内建注解。
内建注解也称为基本注解,位于java.lang包下。
内建注解有三个:
1,检验重写父类方法:@Override
2,标识方法已经过时:@Deprecated
3,取消编译器警告:@SurppressWarnings
(2)元注解。
元注解就是在注解上添加的注解。
位置:元注解位于java.lang.annotation子包中。
作用:用于修饰其他注解。
元注解有四个:
@Retention,@Target,@Documented,@Inherited。
(3)自定义注解。
需要用到关键字@interface来定义。
四个元注解的作用:
@Retention:用来描述被修饰的注解的生命周期。
@Target:用于指定被修饰的注解的适用范围,即被修饰的注解可以用来修饰哪些程序元素。
@Documented:用于指定被修饰的注解将被javadoc工具提取成文档。
@Inherited:用于指定被@Inherited修饰的注解具有继承性。
3 注解的生命周期
注解的生命周期有三种,注解驻留在源文件阶段,字节码文件阶段和内存字节码阶段。
(1)注解被保留到源文件阶段
当javac把.java源文件编译成.class时,就将相应的注解去掉。这种注解的生命周期就维持到源文件阶段。
(2)注解被保留到字节码文件阶段
在JVM通过ClassLoader向内存中加载字节码文件时候,JVM会去掉相应的注解。这种注解的生命周期就维持到字节码文件阶段。
注意:生命周期到源文件阶段和字节码文件阶段的注解,由于JVM执行内存中的字节码时候,相应的注解已经被Javac或者JVM去除,所以无法使用反射来访问相应的注解。
(3)注解被保留到内存中的字节码阶段
JVM运行内存的字节码时候,仍然可能会保留并且执行的某些注解。这种注解的生命周期就维持到内存字节码阶段。
注意:这个阶段,程序可以通过反射访问生命周期到内存字节码阶段的注解。
三个阶段简单表示为:java源文件-->class文件-->内存中的字节码
4 注解的定义反射调用
注解的应用结构图:
注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,得先准备好了这个注解类。就像你要调用某个类,得先有开发好这个类。
代码示例:
执行后,发现控制台上没有打印任何信息。
原因是因为@MyAnnotation注解的@Retention元注解默认值是RetentionPolicy.CLASS,
也就是说@MyAnnotation注解在运行的时候已经被过滤掉了。
解决这个问题的方式就是将@MyAnnotation的@Retention元注解值设置为RetentionPolicy.RUNTIME。
正确的应该是如下:
注意:
@Retention元注解有三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME,分别对应:java源文件-->class文件-->内存中的字节码。
5 为注解增加各种属性
什么是注解的属性?
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是黑马的学生,否则,就不是。
如果还想区分出是黑马哪个班的学生,这时候可以为胸牌再增加一个属性来进行区分。
加了属性的注解效果为:@MyAnnotation(color="red")。
定义基本类型的属性和应用属性:
在注解类中增加String color(); 被添加的注解设置属性值:@MyAnnotation(color="red")。
用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法:
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象。
为属性指定缺省值:
String color() default "yellow";
value属性:String value() default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),
那么可以省略value=部分,例如:@MyAnnotation("lhm")。
注解类的数组类型的属性:
int [] arrayAttr() default {1,2,3};
被添加的注解设置属性值:@MyAnnotation(arrayAttr={2,3,4})。
如果数组属性中只有一个元素,这时候属性值部分可以省略大括号。
枚举类型的属性
EnumTest.TrafficLamp lamp() ;
被添加的注解设置属性值:@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
注解类型的属性:
MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
被添加的注解设置属性值:@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,
可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotation ma = myAnnotation.annotationAttr();
System.out.println(ma.value());
代码示例: