1)所有的注解类型都继承了java.lang.annotation.Annotation这个公共接口 (接口的继承性),说所有注解类型是继承而不是实现,说明所有注解的类型本质就是接口。源码如下:
2)注解 通常单独放置在一行,不影响程序代码的执行。无论是增加或者删除注解,代码都始终执行。public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); Class<? extendsAnnotation> annotationType(); }
- 限定重写:@Override
- 标记过时:@Deprecated
- 抑制警告:@Suppresswarning
- 三种内建注解都被@Retention和@Target来修饰,@Deprecated又被@Documented修饰,可以被javadoc来是识别
- 三种内建注解只有@Deprecated的生命周期被保留到内存字节码阶段,其余三种的生命周期是Java源文件阶段。
- 只有@SuppressWarnings这个注解有一个value的属性变量,是字符串数组类型的。
package java.lang.annotation; public enum ElementType { TYPE, FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE }
- TYPE:类类型、接口类型 (注解类型)、枚举类型声明
- FIELD:成员属性声明 (包括枚举常量成员)
- METHOD:成员方法声明
- PARAMETER:参数声明
- CONSTRUCTOR:构造方法声明
- LOCAL_VARIABLE:局部变量声明
- ANNOTATION_TYPE:注解类型声明
- PACKAEG:包声明
package java.lang.annotation; public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
SOURCE:表示被修饰的注解的生命周期持续到源文件阶段,被Javac编译完成之后就丢弃
CLASS:成员属性声明 (包括枚举常量),被@Retention修饰的注解被Javac编译完成之后会保留,在字节码被JVM运行时丢 弃。注意:@Retention的value的默认取值就是RetentionPolicy.CLASS。
RUNTIME:成员方法声明,被@RUNTIME修饰的注解一直被保留到JVM运行时。
- 对于@Documented、@Retention和@Target可以相互修饰也可以修饰自身。
- @Inherited也被其他三种元注解修饰,但是他不修饰其他元注解,也不修饰自身。
- 四种元注解的生命周期都是到内存字节码阶段。四种元注解的修饰范围只能是注解,不能修饰非注解元素。
- 四种元注解都能被javadoc工具识别并提取成文档。
(2)访问时,以成员变量的形式来进行访问。
3)为注解添加属性//定义时
public @interface MyAnnotation{
String name();
}
//访问时
@MyAnnotation(color="red")
在其他类上使用这个注解,name属性不指定初始值(因为在注解类定义的时候,已经为这个成员变量设置了默认值),但是age必须制定初始值,因为接口中的方法必须被实现。不过在使用该注解时可以根据需要设置新的属性覆盖掉默认值。如下:@interface MyAnnotation{ String name() default "Mike"; int age(); }
(2)调用注解时省略变量名的情况@MyAnnotation(name="John", age=18) public class AnnotationTest{}
- 当注解的定义中只有一个抽象方法且方法名为value的时候,可以在调用这个注解的时候省去变量名,直接赋值。如果这个方法名不是value的话,会报错并提示"Creat attribute value()"。如下:
- 如果注解定义中有多个方法,但是除了一个value()以外,其他方法全部都有默认值,此时也可以直接对value()进行赋值。
4)为注解增加高级属性package cn.itheima; @interface AnnotationOne{ String color() default "red"; int value(); } @AnnotationOne(100) public class MyAnnotation { public static void main(String[] args) { // TODO Auto-generated method stub } }
注意:注解中数组设置默认值和调用的时候,仅接受“{ }”定义的初始值,不接受new关键字声明的数组。否则编译错误。//定义含有数组属性的注解
@interface AnnotationArray { int[] arr() default {4, 5, 6}; }
//调用该注解并为注解赋新值@AnnotationArray(arr ={1, 2, 3}) class AnnotationArrayTest{}//利用反射测试新值是否赋值成功@AnnotationArray(arr={1,2,3}) public class MyAnnotation {public static void main(String[] args) { if(MyAnnotation.class.isAnnotationPresent(AnnotationArray.class)){ AnnotationArray AnArr =(AnnotationArray)MyAnnotation.class .getAnnotation(AnnotationArray.class); printArr(AnArr.arr()); } } public static void printArr(int[] arr){ for (int i : arr) { System.out.println(i); } }}//输出结果:1 2 3 赋值成功。
(3)为注解增加注解属性package cn.itheima; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; // 自定义Gender枚举类型 enum Gender{ male(1), female(0); private int genderIndex =-1; private Gender(int genderIndex){ this.genderIndex =genderIndex; } }
//为注解设定生命周期,便于反射调用 @Retention(value=RetentionPolicy.RUNTIME)
//自定义含有Gender枚举类型的注解类型 @interface AnnotationEnum{ Gender gender() default Gender.male; }
//调用这个含有枚举类型的属性的注解类 @AnnotationEnum(gender =Gender.female) public class MyAnnotation2 { public static void main(String[] args) { if(MyAnnotation2.class.isAnnotationPresent(AnnotationEnum.class)){ AnnotationEnum AnEnum = (AnnotationEnum)MyAnnotation2.class.getAnnotation(AnnotationEnum.class); System.out.println(AnEnum); } } }
4)反射操作注解对象package cn.itheima; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; enum Gender{ male, female; }
@interface AnnotationEnum{ Gender gender() default Gender.male; }
//为注解设定生命周期,便于反射调用 @Retention(value=RetentionPolicy.RUNTIME)
//使用这个AnnotationAnno作为另一个注解中的成员变量 @interface AnnotationAnno{ AnnotationEnum anEnum () default @AnnotationEnum(gender =Gender.female);//注意写法,@不能丢。 }
//调用这个含有注解类型的属性的注解类 @AnnotationAnno() public class MyAnnotation2 { public static void main(String[] args) { if(MyAnnotation2.class.isAnnotationPresent(AnnotationAnno.class)){ AnnotationAnno AnAnuo = (AnnotationAnno)MyAnnotation2.class.getAnnotation(AnnotationAnno.class); System.out.println(AnAnuo); } } }
AnnotatedElement接口专门用于使用反射操作注解, 已知实现子类:AccessibleObject , Class, Package
由于注解是一种标记,仅仅是用来修饰其他程序元素的,因此注解不能单独使用,要依附于具体的程序元素才能使用。所以,让相应的程序片段对应的反射的类(AccessibleObject,Constructor, Filed, Method, Class, Package)去实现AnnotatedElement接口表示这些类的对象通过自身就有能力用反射来来判断自身(程序元素)是否含有注解。
(2)常见操作
- 判定是否存在某一种指定类型的注解
- 获取指定类型的注解对象
- 返回出现在这个程序元素上所有的注解(直接或者间接的都算)
- 返回直接出现在这个程序元素上所有的注解(间接的不算)
注意:使用反射的操作注解的时候,这个注解的生命周期一定要到运行时期。需要对这个注解用元注解@Retention进行修饰,并且把@Retention的成员属性value的值设定为RetentionPolicy.RUNTIME。否则,由于注解默认的是维持到字节码阶段。这样在装载.class文件的时候,这些注解就要被去掉,反射技术无法访问到这些字节码文件注解。