注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。
作用分类:
1、编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
2、代码分析:通过代码里标识的注解对代码进行分析【使用反射】
3、编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
JDK中预定义的注解
JDK1.5之后内部提供的三个注解:
- @Deprecated意思是“废弃的,过时的”
- @Override意思是“重写、覆盖”
- @SuppressWarnings意思是“压缩警告”,作用:用于抑制编译器产生警告信息。
接下来我们依次介绍一下。
@Deprecated
该注解标注的内容,表示已过时,我们先定义两个方法,show1()方法因为过时了,功能跟不上需求了,所以我们在它上面打了一个@Deprecated注解,show2()是一个更强大的功能。
@Deprecatedpublicvoidshow1(){//发现过时了,功能跟不上需求了}publicvoidshow2(){//功能更加强大的方法}
我们在调用的地方发现有一条红色的横线,意思就是说这个方法已经废弃了,不建议使用了。那既然过时了你直接把这个方法删了不就得了,又要留着又要告诉你不要使用,岂不是多此一举?其实这么做的原因就是为了做到向下兼容,你把它删了,那之前老代码引用这个方法的地方都得报错,所以我们需要通过这种方法才做到平滑过渡。
还有JDK中自带的Data类,getYear()方法上也有根红色的横线,这个也是因为打了@Deprecated的注解,我们点进去看下确实如此。这样的例子还有很多,小伙们在平时的开发中注意观察下,过期的方法就不要使用啦...
@Override
检测被该注解标注的方法是否是继承自父类(接口)的,这个也比较简单,它可以避免方法名和参数写错,起到检查的作用。
@OverridepublicStringtoString(){return"xxxx";}复制代码
@SuppressWarnings
压制警告,我们看下图str没有用到,编辑器提示了一个粉红色的波浪线,并且告诉我们variable'str'isneverused
这只是其中一个例子,还有各种各样的警告,这时候我们会觉得很烦,怎么办呢,我们就可以用@SuppressWarnings,告诉编辑器不要给我提示了,一般我们传递参数all,@SuppressWarnings("all"),此时波浪线就没有了。
当然如果不想抑制所有的话,有些警告我们想看到的话,也可以抑制具体的值,如下表所示:
参数 | 说明 | 说明 |
all | tosuppressallwarnings | 抑制所有警告 |
boxing | tosuppresswarningsrelativetoboxing/unboxingoperations | 抑制装箱、拆箱操作时候的警告 |
cast | tosuppresswarningsrelativetocastoperations | 抑制映射相关的警告 |
dep-ann | tosuppresswarningsrelativetodeprecatedannotation | 抑制启用注释的警告 |
deprecation | tosuppresswarningsrelativetodeprecation | 抑制过期方法警告 |
fallthrough | tosuppresswarningsrelativetomissingbreaksinswitchstatements | 抑制确在switch中缺失breaks的警告 |
finally | tosuppresswarningsrelativetofinallyblockthatdon’treturn | 抑制finally模块没有返回的警告 |
hiding | tosuppresswarningsrelativetolocalsthathidevariable | 抑制与隐藏变量的局部变量相关的警告 |
incomplete-switch | tosuppresswarningsrelativetomissingentriesinaswitchstatement | 忽略没有完整的switch语句 |
nls | tosuppresswarningsrelativetonon-nlsstringliterals | 忽略非nls格式的字符 |
null | tosuppresswarningsrelativetonullanalysis | 忽略对null的操作 |
rawtypes | tosuppresswarningsrelativetoun-specifictypeswhenusinggenericsonclassparams | 使用generics时忽略没有指定相应的类型 |
restriction | tosuppresswarningsrelativetousageofdiscouragedorforbiddenreferences | 抑制与不鼓励或禁止引用的使用有关的警告 |
serial | tosuppresswarningsrelativetomissingserialVersionUIDfieldforaserializableclass | 忽略在serializable类中没有声明serialVersionUID变量 |
static-access | tosuppresswarningsrelativetoincorrectstaticaccess | 抑制不正确的静态访问方式警告 |
synthetic-access | tosuppresswarningsrelativetounoptimizedaccessfrominnerclasses | 抑制子类没有按最优方法访问内部类的警告 |
unchecked | tosuppresswarningsrelativetouncheckedoperations | 抑制没有进行类型检查操作的警告 |
unqualified-field-access | tosuppresswarningsrelativetofieldaccessunqualified | 抑制没有权限访问的域的警告 |
unused | tosuppresswarningsrelativetounusedcode | 抑制没被使用过的代码的警告 |
除了JDK自带的注解,我们还可以编写自定义注解来满足我们平时开发中的一些需要,这个也是本篇文章的重点,创建注解的格式如下:
//元注解public@interface注解名称{//属性列表}复制代码
自定义的注解反编译后的内容,注解的本质其实就是一个接口,继承Annotation父接口
publicinterfaceMyAnnoextendsjava.lang.annotation.Annotation{}复制代码
下面写了一个注解的例子,我们来依次介绍下。。
元注解
在注解类上使用另一个注解类,那么被使用的注解类就称为元注解,JDK中给我们提供的4个元注解
@Retention
@Retention注解决定MyAnnotation注解的生命周期,描述注解被保留到的阶段,SOURCE<CLASS<RUNTIME
- SOURCE:让MyAnnotation注解只在java源文件中存在,编译成.class文件后注解就不存在了
- CLASS:让MyAnnotation注解在java源文件(.java文件)中存在,编译成.class文件后注解也还存在,被MyAnnotation注解类标识的类被类加载器加载到内存中后MyAnnotation注解就不存在了
- RUNTIME:让MyAnnotation这个注解的生命周期一直到程序运行时都存在
@Target
@Target元注解决定了一个注解可以标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分
- ElementType.TYPE:可以作用在类上
- ElementType.METHOD:可以作用在方法上
- ElementType.FIELD:可以作用在成员变量上
@Documented
描述注解是否被抽取到JavaDocapi中
@inherited
描述注解是否可以被子类继承
属性
上文中说过注解的本质就是接口,所以属性就是在接口中定义的抽象方法。
语法:类型属性名();返回的结果必须是如下类型:
- 基本数据类型
- String类型
- 枚举类型
- 注解
- 以上类型的数组
上图中自定义了一个color的属性,在使用中如下:
//应用MyAnnotation注解的color属性@MyAnnotation(color="red")publicclassAnnotationTest{publicstaticvoidmain(String[]args){//用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法MyAnnotationannotation=(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);//输出redSystem.out.println(annotation.color());}}复制代码
属性赋值一些注意点
1、如果定义的属性时,使用default关键字给属性默认初始值,可以在使用注解时不赋值
//指定默认值在使用注解的时候没有给该属性赋值,那么就使用默认值Stringcolor()default"blue";复制代码
2、如果一个注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉value=部分。
@SuppressWarnings("deprecation")@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceMyAnnotation{Stringcolor()default"blue";//为属性指定缺省值Stringvalue();//定义一个名称为value的属性}@MyAnnotation("xxx")//使用的时候等价于@MyAnnotation(value="xxx")复制代码
3、数组赋值的时候,值使用{}包裹,如果数组中只有一个值,那么{}可以省略
//数组类型的属性int[]arrayAttr()default{1,2,4};//使用的时候加上{}@MyAnnotation(arrayAttr={2,4,5})//如果数组属性只有一个值,这时候属性值部分可以省略大括号@MyAnnotation(arrayAttr=2),复制代码
4、枚举
//枚举类型的属性EumTrafficLamplamp()defaultEumTrafficLamp.RED;//使用注解@MyAnnotation(lamp=EumTrafficLamp.GREEN)复制代码
5、注解的属性值又是一个注解
MetaAnnotationannotationAttr()default@MetaAnnotation("aaa");复制代码
自定义注解的案例
下面我们通过注解的方式来实现一个通过反射创建一个对象的功能。
自定义注解:
@OverridepublicStringtoString(){return"xxxx";}复制代码0
定义一个实体类:
@OverridepublicStringtoString(){return"xxxx";}复制代码1
通过反射创建对象:
@OverridepublicStringtoString(){return"xxxx";}复制代码2
执行一下,好,大功告成!
自定义注解一般我们应用在拦截器或者AOP里面,用来设计自己的框架,使代码看起来非常优雅。