从JDK5.0开始, Java增加了对元数据(MetaData)的支持,也就是 Annotation(注解)。
Annotation其实就是代码里的特殊标记,它用于替换配置文件,也就是说,传统方式通过配置文件告知类如何运行,有了注解技术后,开发人员可以通过注解告知类如何运行。在Java技术里注解的典型利用是:可以通过反射技术去得到类里面的注解,以决定怎样去运行类。
掌握注解技术的要点:
@SuppressWarnings:抑制编译器正告。
首先编写1个AnnotationTest类,先通过@SuppressWarnings
的利用让大家认识和了解1下注解,通过System.runFinalizersOnExit(true);
的编译正告引出@SuppressWarnings("deprecation")
。
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
}
@Deprecated:用于表示某个程序元素(类,方法等)已过时。
接着直接在刚才的类中增加1个方法,并加上@Deprecated
标注,在另外1个类中调用这个方法。
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
public boolean equals(Reflect other)
方法与HashSet结合讲授。总结:注解相当于1种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有没有何种标记,看你有甚么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数和局部变量上。
注解就相当于1个你的源程序中要调用的1个类,要在源程序中利用某个注解,得先准备好了这个注解类,就像你要调用某个类,得先要开发好这个类。
定义新的Annotation类型使用@interface关键字。
定义1个最简单的注解:
public @interface ItcastAnnotation {
}
把它加在某个类上:
@ItcastAnnotation
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
用反射进行测试AnnotationTest的定义上是不是有@ItcastAnnotation:
@ItcastAnnotation
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation);
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
发现控制台无任何反应,根据反射测试的问题,引出@Retention
元注解的讲授。
@Retention元注解
只能用于修饰1个Annotation的定义, 用于指定该Annotation可以保存的域,@Rentention包括1个RetentionPolicy类型的成员变量,通过这个变量指定域。其有3种取值:
分别对应:java源文件→class文件→内存中的字节码。也即1个Java类具有3种状态:
编译 JVM装载进内存
.java -------------------> .class -------------------> 运行时(内存里面的java类)
对@Retention元注解更加细致的解释:
所以,1个注解的生命周期有3个阶段。
我们定义的简单注解修改成以下:
@Retention(RetentionPolicy.RUNTIME)
public @interface ItcastAnnotation {
}
就可以正确打印了。
结论:我们写1个注解的目的,主要是用来替换配置文件,我们希望这个类在运行时取得注解配置的信息,来运行我这个类,想要注解配置的信息能够让类在运行时获得到,那就1定要把这个注解声明在运行时。
思考:@Override、@SuppressWarnings和@Deprecated这3个注解的属性值分别是甚么?
答:
@Override→RetetionPolicy.SOURCE
@SuppressWarnings→RetetionPolicy.SOURCE
@Deprecated→RetetionPolicy.RUNTIME
@Target元注解
指定注解用于修饰类的哪一个成员。@Target包括了1个名为value,类型为ElementType的成员变量。如若声明注解时,没指定@Target,默许该注解可以作用在类的所有成员上。
Target的默许值为任何元素,设置Target等于ElementType.METHOD
,原来加在类上的注解就报错了,改成用数组方式设置{ElementType.METHOD,ElementType.TYPE}
就能够了,表示此注解既可以在方法上使用,也可在类上使用。
注意:元注解和其枚举属性值不用记,只要会看JDK提供那几个基本注解的API帮助文档的定义或其源代码,按图索骥便可查到,或直接看java.lang.annotation包下面的类。
注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描写。
@ItcastAnnotation(color="red")
定义基本类型的属性和利用属性
在注解类中增加String color()
:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color();
}
用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:
@ItcastAnnotation(color="red")
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color()); // red
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
可以认为上面这个@ItcastAnnotation(color=”red”)是ItcastAnnotation类的1个实例对象。
为属性指定缺省值:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
}
value属性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
}
用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:
@ItcastAnnotation(color="red", value="abc")
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.value()); // abc
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
注意:如果注解中有1个名称为value的属性,且你只想设置value属性(即其他属性都采取默许值或你只有1个value属性),那末可以省略value=
部份,例如:@ItcastAnnotation("lhm")
。
数组类型的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
}
用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:
@ItcastAnnotation(color="red", value="abc", arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.arrayAttr().length); // 1
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
注意:如果数组属性中只有1个元素,这时候候属性值部份可以省略大括号。
枚举类型的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
}
用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:
@ItcastAnnotation(color="red", value="abc", arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.lamp().nextLamp().name()); // GREEN
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
注解类型的属性:
我们再定义1个注解MetaAnnotation
:
public @interface MetaAnnotation {
String value();
}
接着再定义1个注解类型的属性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}
枚举和注解都是特殊的类,不能用new创建它们的实例对象,创建枚举的实例对象就是在其中增加元素。在程序中如何创建出1个注解的实例对象啊?直接用@放上1个标记便可,例如:@MetaAnnotation("lhm")
。
用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"), color="red", value="abc", arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.annotationAttr().value()); // flx
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
可以认为@ItcastAnnotation是ItcastAnnotation类的1个实例对象,一样的道理,可以认为@MetaAnnotation是MetaAnnotation类的1个实例对象。
注解的详细语法可以通过看java语言规范了解,即看java的language specification,可知道注解的属性类型包括:基本数据类型,String,Class,枚举,其他注解,和这些类型的数组。