一、认识注解
注解(Annotation)很重要,现在的开发模式都是基于注解的,JPA是基于注解的,从Spring基于注解的,从Hibernate也是基于注解的,注解是JDK1.5之后才有的新特性
JDK1.5之后内部提供的三个注解
@Deprecated 意思是“废弃的,过时的”
@Override 意思是“重写、覆盖”
@SuppressWarnings 意思是“压缩警告”,作用:用于抑制编译器产生警告信息
通过例子,用一下这三个jdk原生注解,演示注解的基本应用:
public class AnnotationTest {
/**
* @param args
*/
//这里就是注解,称为压缩警告,这是JDK内部自带的一个注解,一个注解就是一个类,在这里使用了这个注解就是创建了
SuppressWarnings类的一个实例对象
public static void main(String[] args) {
sayHello();
//这里的sayHello()方法画了一条横线表示此方法已经过时了,不建议使用了
@SuppressWarnings("unused")
String str = "sdfd";
}
@Deprecated //这也是JDK内部自带的一个注解,意思就是说这个方法已经废弃了,不建议使用了
public static void sayHello(){
System.out.println("hi,xxxx");
}
@Override //这也是JDK1.5之后内部提供的一个注解,意思就是要重写(覆盖)JDK内部的toString()方法
public String toString(){
return "xxxx";
}
}
总结:注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。
注解就相当于一个你的源程序要调用一个类,在源程序中应用某个注解,得事先准备好这个注解类。就像你要调用某个类,得事先开发好这个类。
二、自定义注解及其应用
自定义一个最简单的注解: public @interface MyAnnotation{}
/**
* 这是一个自定义的注解(Annotation)类 在定义注解(Annotation)类时使用了另一个注解类Retention
* 在注解类上使用另一个注解类,那么被使用的注解类就称为元注解
@Retention(RetentionPolicy.RUNTIME)
//Retention注解决定MyAnnotation注解的生命周期
/*
* @Retention(RetentionPolicy.SOURCE)
* 这个注解的意思是让MyAnnotation注解只在java源文件中存在,编译成.class文件后注解就不存在了
* @Retention(RetentionPolicy.CLASS)
* 这个注解的意思是让MyAnnotation注解在java源文件(.java文件)中存在,编译成.class文件后注解也还存在,
* 被MyAnnotation注解类标识的类被类加载器加载到内存中后MyAnnotation注解就不存在了
* Retention注解括号中的"RetentionPolicy.RUNTIME"意思是让MyAnnotation这个注解的生命周期一直到程序运行时都存在
*/
@Target( { ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
//自定义注解时,以上各两个注解一般都会加上,如果不设第二个注解时,测试是否有注解时会报没有注解
//Target注解决定MyAnnotation注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分
public @interface MyAnnotation {
}
把自定义的注解加到某个类上:
@MyAnnotation
public class AnnotationUse{
}
用反射测试进行测试AnnotationUse的定义上是否有@MyAnnotation
@MyAnnotation
//这里是将新创建好的注解类MyAnnotation标记到AnnotationUse类上
public class AnnotationUse {
public static void main(String[] args) {
// 这里是检查Annotation类是否有注解,这里需要使用反射才能完成对Annotation类的检查
if (AnnotationUse.class.isAnnotationPresent(MyAnnotation.class)) {
/*
* MyAnnotation是一个类,这个类的实例对象annotation是通过反射得到的,这个实例对象是如何创建的呢?
* 一旦在某个类上使用了@MyAnnotation,那么这个MyAnnotation类的实例对象annotation就会被创建出来了
* 假设很多人考驾照,教练在有些学员身上贴一些绿牌子、黄牌子,贴绿牌子的表示送礼送得比较多的,
* 贴黄牌子的学员表示送礼送得比较少的,不贴牌子的学员表示没有送过礼的,通过这个牌子就可以标识出不同的学员
* 教官在考核时一看,哦,这个学员是有牌子的,是送过礼给他的,优先让有牌子的学员过,此时这个牌子就是一个注解
* 一个牌子就是一个注解的实例对象,实实在在存在的牌子就是一个实实在在的注解对象,把牌子拿下来(去掉注解)注解对象就不存在了
*/
MyAnnotation annotation = (MyAnnotation) AnnotationUse.class
.getAnnotation(MyAnnotation.class);
System.out.println(annotation);// 打印MyAnnotation对象,这里输出的结果为:@cn.itcast.day2.MyAnnotation()
}
}
}
三、@Retention元注解
根据反射的测试的问题,引出@Retention元注解的讲解:其三种取值: RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME分别对应:Java源文件(.java文件)---->.class文件---->内存中的字节码-
@Retention(RetentionPolicy.SOURCE)
这个注解的意思是让MyAnnotation注解只在java源文件中存在,编译成.class文件后注解就不存在了 -
@Retention(RetentionPolicy.CLASS)
这个注解的意思是让MyAnnotation注解在java源文件(.java文件)中存在,编译成.class文件后注解也还存在,
被MyAnnotation注解类标识的类被类加载器加载到内存中后MyAnnotation注解就不存在了 -
Retention注解括号中的"RetentionPolicy.RUNTIME"意思是让MyAnnotation这个注解的生命周期一直到程序运行时都存在
四、@Target元注解
@Target元注解决定了一个注解可以标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分,@Target默认值为任何元素(成分) 例如1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
2 @Retention(value=SOURCE)
3 public @interface SuppressWarnings
五、为注解增加属性
注解可以看成是一种特殊的类,既然是类,那自然可以为类添加属性1.添加属性
语法:类型 属性名();
@Retention(RetentionPolicy.RUNTIME)
//Retention注解决定MyAnnotation注解的生命周期
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
/**
* 定义基本属性
* @return
*/
String color();
}
其实从代码的写法上来看,注解更像是一种特殊的接口,注解的属性定义方式就和接口中定义方法的方式一样,而应用了注解的类可以认为是实现了这个特殊的接口
2.应用属性
@MyAnnotation(color="red")//应用MyAnnotation注解的color属性
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出red
//全部利用反射的到注解中值
//得到该类上的注解
Annotation annotation = clazz.getAnnotation(MyAnnotation.class);
//得到注解的类
Class aclazz = annotation.getClass();
try {
//得到注解类中的方法
Method method = aclazz.getMethod("color");
//执行注解类中的方法得到值
String value = (String) method.invoke(annotation);
System.out.println(value);// 输出red
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3.为属性指定缺省值(默认值)
语法:类型 属性名() default 默认值;
@Retention(RetentionPolicy.RUNTIME)
//Retention注解决定MyAnnotation注解的生命周期
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
String color() default "blue";//为属性指定缺省值
}
@MyAnnotation
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出color属性的默认值:blue
}
}
4.value属性
如果一个注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉“value=”部分。例如:@SuppressWarnings("deprecation")
@Retention(RetentionPolicy.RUNTIME)
//Retention注解决定MyAnnotation注解的生命周期
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
String color() default "blue";//为属性指定缺省值
String value();//定义一个名称为value的属性
}
@MyAnnotation("xxx")//等价于@MyAnnotation(value="xxx")
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出color属性的默认值:blue
System.out.println(annotation.value());
}
}
六、为注解增加高级属性
1、数组类型的属性
- 增加数组类型的属性:int[] arrayAttr() default {1,2,4};
- 应用数组类型的属性:@MyAnnotation(arrayAttr={2,4,5})
- 如果数组属性只有一个值,这时候属性值部分可以省略大括号,如:@MyAnnotation(arrayAttr=2),这就表示数组属性只有一个值,值为2
2、枚举类型的属性
- 增加枚举类型的属性:EumTrafficLamp lamp() default EumTrafficLamp.RED;
- 应用枚举类型的属性:@MyAnnotation(lamp=EumTrafficLamp.GREEN)
3、注解的属性值又是一个注解
- MetaAnnotation annotationAttr() default @MetaAnnotation(“xdp”);
七、注解综合测试
EumTrafficLamp.java
public enum EumTrafficLamp {
RED,//红
YELLOW,//黄
GREEN//绿
}
public @interface MetaAnnotation {
String value();//元注解MetaAnnotation设置有一个唯一的属性value
}
@Retention(RetentionPolicy.RUNTIME)
//Retention注解决定MyAnnotation注解的生命周期
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
String color() default "blue";//为属性指定缺省值
String value();//定义一个名称为value的属性
//添加一个int类型数组的属性
int[] arrayAttr() default {1,2,4};
EumTrafficLamp lamp() default EumTrafficLamp.RED;
//为注解添加一个注解类型的属性,并指定注解属性的缺省值
MetaAnnotation annotationAttr() default @MetaAnnotation("xdp");
}
@MyAnnotation(
color="red",
value="xxx",
arrayAttr={3,5,6},
lamp=EumTrafficLamp.GREEN,
annotationAttr=@MetaAnnotation("gacl")
)
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 这里是检查Annotation类是否有注解,这里需要使用反射才能完成对Annotation类的检查
*/
if(MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
/**
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
* MyAnnotation是一个类,这个类的实例对象annotation是通过反射得到的,这个实例对象是如何创建的呢?
* 一旦在某个类上使用了@MyAnnotation,那么这个MyAnnotation类的实例对象annotation就会被创建出来了
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出color属性的默认值:red
System.out.println(annotation.value());//输出value属性的默认值:孤傲苍狼
System.out.println(annotation.arrayAttr().length);//这里输出的数组属性的长度的结果为:3,数组属性有三个元素,因此数组的长度为3
System.out.println(annotation.lamp());//这里输出的枚举属性值为:GREEN
System.out.println(annotation.annotationAttr().value());//这里输出的注解属性值:gacl
MetaAnnotation ma = annotation.annotationAttr();//annotation是MyAnnotation类的一个实例对象
System.out.println(ma.value());//输出的结果为:gacl
}
}
}