目录:
从 JDK5 开始,Java 增加了对元数据(MetaData)的支持,也就是 Annotation。Annotation 其实就是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
1、基本 Annotation
Java 提供的 5 个基本 Annotation 如下:
1)Override,2)Deprecated, 3)SuppressWarnings, 4)SafeVarargs, 5)FunctionalInterface
1.1、限定重写父类方法:@Override
@Override 的用法举例:
public classAnimal {public voideat() {
System.out.println("Animal 的 eat() 方法");
}
}class Cat extendsAnimal {//使用 @Override 注解指定下面的 eat() 方法必须是重写父类方法
@Overridepublic voideat() {//super.eat();
System.out.println("Cat 的 eat() 方法");
}
}
@Override 的源码:
packagejava.lang;import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)public @interface Override {}
@Target(ElementType.METHOD):表示 @Override 注解只能标注在方法上。
@Retention(RetentionPolicy.SOURCE):表示 @Override 注解只会保留在源代码中,编译器直接丢弃(class 文件中不保留)。
1.2、标记已过时:@Deprecated
@Deprecated 用于表示某个程序元素(类、方法等)已过时,当程序使用已过时的类、方法时,编译器将会给出警告。
@Deprecated 使用举例:使用已过时的方法 eat(),IDE 会在该方法上加上中划线 “—” 的标识
1.3、抑制编译器警告:@SuppressWarnings
@SuppressWarnings 使用举例:
1.4、Java7 的“堆污染”警告和 @SafeVarargs
下面的代码引发错误的原因成为“堆污染”,当把一个不带泛型的对象赋给一个带泛型的对象时,往往就会发生这种“堆污染”。
public static voidmain(String[] args) {
List list= new ArrayList();
list.add(10);//将 list 赋给 list2,编译运行都完全正常
List list2 =list;//但只要访问list2里面的元素,就会引起运行时异常
/*Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String
at com.oy.Animal.main(Animal.java:11)*/System.out.println(list2.get(0));
}
对于形参个数可变的方法,该形参又是泛型,将更容易导致“堆污染”。如下面的代码,listArray 是可变参数,相当于数组,但是Java不支持泛型数组,所以只能把 List... 当成 List[]来处理,这样就发生了“堆污染”,当在访问 listArray 的元素时引起运行时异常。
public classDemo {public static voidmain(String[] args) {
method(Arrays.asList("aaa"), Arrays.asList("bbb"));
}public static void method(List... listArray) {
List[] array=listArray;
array[0] = Arrays.asList(1);//java.lang.Integer cannot be cast to java.lang.String
System.out.println(listArray[0].get(0));
}
}
但有时候我们知道代码没有问题,我们不想看到这个警告,可以使用 @SafeVarargs 来压制这个警告
1.5、Java8 新增的用于标识(限定)函数式接口:@FunctionalInterface
如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),该接口就是函数式接口。@FunctionalInterface 就是用来指定某个接口必须是函数式接口。
@FunctionalInterface 注解只是告诉编译器检查这个接口,保证这个接口只能由一个抽象方法,否则就会报编译出错。
2、JDK 的元 Annotation
JDK 提供了 5 个Meta Annotation(元注解),用于修饰其他的 Annotation 定义,即:
1) Retention, 2) Target, 3) Documented, 4) Inherited, 5) Repeatable。
2.1、注解的保留策略限定:@Retention
@Retention 源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)public @interfaceRetention {
RetentionPolicy value();
}
@Retention 只能用于修饰 Annotation 定义,用于指定被修饰的 Annotation 可以保留多长时间,@Retention 包含一个 RetentionPolicy 类型的 value 成员变量,所以使用 @Retention 时必须为该 value 成员变量指定值。value 成员变量的值只能是如下三个:
RetentionPolicy.SOURCE: Annotation 只保留在源代码中,编译器直接丢弃这种注解。
RetentionPolicy.CLASS: 编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM 不可获取该 Annotation 信息。这是默认值。
RetentionPolicy.RUNTIME: 编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM 也可获取该 Annotation 信息,程序可以通过反射获取该 Annotation 信息。
定义一个 “编译时注解”:
//RetentionPolicy.SOURCE: 表示此注解只保留在源代码中,编译器直接丢弃这种注解//通常称这种注解为 “编译时注解”
@Retention(RetentionPolicy.SOURCE)public @interfaceMyTag {
}
定义一个 “运行时注解”:
//RetentionPolicy.SOURCE: 编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM 也可获取该 Annotation 信息,//程序可以通过反射获取该 Annotation 信息。//通常称这种注解为 “运行时注解”
@Retention(RetentionPolicy.RUNTIME)public @interfaceMyTag {
}
2.2、注解的作用目标限定:@Target
@Target 源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)public @interfaceTarget {
ElementType[] value();
}
@Target 包含一个 ElementType[] 类型的 value 成员变量,该成员变量的值只能为如下:
public enumElementType {/**Class, interface (including annotation type), or enum declaration*/TYPE,/**Field declaration (includes enum constants)*/FIELD,/**Method declaration*/METHOD,/**Formal parameter declaration*/PARAMETER,/**Constructor declaration*/CONSTRUCTOR,/**Local variable declaration*/LOCAL_VARIABLE,/**Annotation type declaration*/ANNOTATION_TYPE,/**Package declaration*/PACKAGE,/*** Type parameter declaration</