认识注解
注解(Annotation)很重要,现在的开发模式都是基于注解,已经是一种趋势,注解是JDK1.5之后才有的新特性,JDK1.5之后提供的三个注解:
- @Deprecated :废弃的,过时的
- @Override :重写、覆盖
- @SuppressWarnings:压缩警告
栗子 – 注解的应用
/**
* @className: BlogDemo
* @description: 此类用来掩饰注解(Annotation)的应用,注解也是JDK1.5新增加的特性之一
* JDK1.5内部提供的三种注解是:@SuppressWarnings(":deprecation")、@Deprecated、@Override
* @author: Ian
* @create: 2018-08-11 16:18
**/
public class BlogDemo {
/**
* 这里就是注解,称谓压缩警告,这是JDK内部自带的一个注解,一个注解就是一个类,在这里使用了这个注解就是创建了SuppressWarnings类的一个实例对象
*/
@SuppressWarnings( ":deprecation" )
public static void main(String[] args){
/*
下面这个runFinalizersOnExit()方法画了一条横线表示此方法已经过时,不建议使用
*/
System.runFinalizersOnExit( true );
}
/**
* 这个也是JDK内部自带的一个注解,意思是已经废弃,不建议使用
*/
@Deprecated
public static void hello(){
System.out.println("hi,Ian");
}
/**
* 这也是JDK1.5之后内部提供的一个注解,意思就是重写(覆盖)JDK内部的toString()方法
*/
@Override
public String toString(){
return "Ian";
}
}
总结:注解(Annotation)相当于一种标记,在程序中加入注解就等于程序打上某种标记,没有加,则等于没有任何标记,javac编译器、开发工具和其它程序可以通过反射来了解你的类及各种元素上有无任何标记,看你程序有什么标记,就去干相应的事,标记可以加在包、类、属性、方法、方法形参以及局部变量上。
注解就相当于一个你的源程序要调用一个类,在源程序中用用某个注解,得事先准备好这个注解类。就像你要调用某个类,得事先开发好这个类。
自定义注解及其应用
/**
* 这是一个自定义的注解(Annotation)类,在定义注解类时使用了另一个注解类Retention
* 在注解类上使用另一个注解类,那么被使用的注解类就称为元注解
* @author: Ian
*
* Rentention注解决定MyAnnotation注解的生命周期
*/
@Retention( RetentionPolicy.RUNTIME )
//Target 注解决定MyAnnotation注解可以加上哪些成分上面,比如加在类身上,或者属性身上,或者方法等等
@Target({ElementType.METHOD, ElementType.TYPE})
/*
@Retention(RetentionPolicy.ROURCE)
这个注解的意思是让MyAnnotation注解只在java源文件中存在,编译成.class文件之后注解就不存在了
@Retention(RetentionPolicy.CLASS)
这个注解的意思是让MyAnnotation注解在Java源文件(.java)中存在,编译成.class文件之后注解也还存在,
被MyAnnotation注解类标识的类加载器加载到内存中后MyAnnotation注解就不存在了。
这里是在注解类MyAnnotation上使用另一个注解类,这里的Retention称为元注解。
Retention注解括号中的“RetentionPolicy.RUNTIME”意思是让MyAnnotation这个注解的声明周期一直在程序运行时都存在
*/
public @interface MyAnnotation{
}
把自定义注解加到某个类上并用反射进行测试AnnotationUser的定义上是否有@MyAnnotation:
package cn.gacl.annotation;
2 @MyAnnotation
3 //这里是将新创建好的注解类MyAnnotation标记到AnnotaionTest类上
4 public class AnnotationUse {
5 public static void main(String[] args) {
6 // 这里是检查Annotation类是否有注解,这里需要使用反射才能完成对Annotation类的检查
7 if (AnnotationUse.class.isAnnotationPresent(MyAnnotation.class)) {
8 /*
9 * MyAnnotation是一个类,这个类的实例对象annotation是通过反射得到的,这个实例对象是如何创建的呢?
10 * 一旦在某个类上使用了@MyAnnotation,那么这个MyAnnotation类的实例对象annotation就会被创建出来了
11 * 假设很多人考驾照,教练在有些学员身上贴一些绿牌子、黄牌子,贴绿牌子的表示送礼送得比较多的,
12 * 贴黄牌子的学员表示送礼送得比较少的,不贴牌子的学员表示没有送过礼的,通过这个牌子就可以标识出不同的学员
13 * 教官在考核时一看,哦,这个学员是有牌子的,是送过礼给他的,优先让有牌子的学员过,此时这个牌子就是一个注解
14 * 一个牌子就是一个注解的实例对象,实实在在存在的牌子就是一个实实在在的注解对象,把牌子拿下来(去掉注解)注解对象就不存在了
15 */
16 MyAnnotation annotation = (MyAnnotation) AnnotationUse.class
17 .getAnnotation(MyAnnotation.class);
18 System.out.println(annotation);// 打印MyAnnotation对象,这里输出的结果为:@cn.itcast.day2.MyAnnotation()
19 }
20 }
21 }
把自定义的注解加到某个类上并用反射测试进行测试AnnotationUse的定义上是否有@MyAnnotation:
@MyAnnotation
//这里是将新创建好的注解类MyAnnotation标记到AnnotaionTest类上
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注解说明
当在Java源程序上加了一个注解,这个Java源程序要由javac去编译,javac把java源文件编译成.class文件,在编译成class时可能会把Java源程序上的一些注解给去掉,java编译器在处理java源程序时,可能会认为这个注解没有用了,于是就把这个注解去掉,那么此时在编译好的class就找不到该注解,这是编译器编译Java源程序时对注解进行处理的第一种可能情况,假设Java编译器在把Java源程序编译成class时,没有把注解去掉,那么此时在编译好的class中就可以找到注解,当程序使用编译好的class文件时,需要用类加载器把class文件加载到内存中并进行处理,如安全检查,处理完以后得到的最终在内存中的二进制的东西才是字节码,类加载器把class文件加载到内存中时也有转换,转换时是把class文件中的注解保留下来,这也有说法,所以说一个注解的生命周期有三个阶段:java源文件、class文件、内存中的字节码分别都是一个阶段,javac把java源文件编译成.class文件时,有可能去掉里面的注解,类加载器把.class文件加载到内存时也有可能去掉里面的注解,因此在自定义注解时就可以使用Retention注解知名自定义注解的生命周期,自定义注解的生命周期是在RetentionPolicy.SOURCE阶段(java源文件阶段),还是在RetentionPolicy.CLASS阶段(class文件阶段),或者是在RetentionPolicy.RUNTIME阶段(内存中的字节码运行时阶段),根据JDK提供的API可以知道默认是在RetentionPolicy.CLASS阶段 (JDK的API写到:the retention policy defaults to RetentionPolicy.CLASS)
下面看看@Deprecated、@Override、@SuppressWarnings这三个注解的@Retention注解的属性值分别是什么吧
@Deprecated
java API中是这样定义的@Deprecated的
@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
@Override
Java API中是这样定义的@Override的
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
@Override 是给javac看的,编译完以后@Override就没有价值了,@Override注解在源代码中有用,编译成.class文件后@Override注解就没有用了,因此@Override的Retention的属性值是RetentionPolicy.SOURCE
SuppressWarnings
Java API中是这样定义的@SuppressWarnings的
@Target(value{TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
@SuppressWarnings是给javac(java编译器)看的,编译器编译完java文件后,@SuppressWarnings注解就没有用了,所以@SuppressWarnings的Retention的属性值是RetentionPolicy.SOURCE
Target元注解
@Target元注解决定了一个注解可以标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分,@Target默认值为任何元素(成分) 例如:
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
为注解增加属性
注解可以看成是一种特殊的类,既然是类,那自然可以为类添加属性
添加属性
语法:类型 属性名();
//Retention注解决定MyAnnotation注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation{
/*
定义基本属性
*/
String color();
}
其实从代码的写法上来看,注解更像是一种特殊的接口,注解的属性定义方式就和接口中定义方法的方式一样,而应用了注解的类可以认为是实现了这个特殊的接口
应用属性
//应用MyAnnotation注解的color属性
@MyAnnotation(color="red")
public class MyAnnotationTest{
public static void main(String[] args){
/*
用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation)MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
//输出red
System.out.println(annotation.color());
}
}
为属性指定缺省值(默认值)
语法:类型 属性名() default 默认值;
//Retention注解决定MyAnnotation注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
@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
}
}
value属性
如果一个注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉“value=”部分。 例如:@SuppressWarnings(“deprecation”)
@Retention(RetentionPolicy.RUNTIME)
@Terget({ElementType.METHOD, ElementType.Type})
public @interface MyAnnotation{
//为属性指定缺省值
String color() default "blue";
//定义一个名称为value的属性
String value();
}
@MyAnnotation("Ian")
public class MyAnnotationTest{
//用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
MyAnnotation annotation = (MyAnnotation)MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
//输出color属性的默认值:blue
System.out.println(annotation.class);
//输出Ian
System.out.println(annotation.value());
}