本文共 7189 字,大约阅读时间需要 23 分钟。
注解,如果看他的定义,可能会很迷糊
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.
元数据,为程序提供数据,不是程序的一部分,对代码没有直接的影响
让人看得很懵
首先,注解会带有信息,或是告诉编译器
@Override public String elementName() { return null; }
上面这个应该是最常见到的注解了
Override 覆盖,也就是写一些新的东西把原来的内容覆盖掉
通常应用于接口的实现
其作用是:
告诉编译器,下面这个方法是需要被 “覆盖” 的
如果这个注解下的方法,你没有重新写一些新内容进行覆盖
编译器会提示你:(你用像Eclipse 这样的IDE时,马上就会有高亮报错了)
The method xx() of type X must override or implement a supertype method
就是提醒你得去override 的意思
建 议 直 接 去 I D E 里 试 试 , 这 真 难 以 用 文 字 表 达 \color{blue}{建议直接去IDE里试试,这真难以用文字表达} 建议直接去IDE里试试,这真难以用文字表达
@Override是Java内置的作用于Java代码的注解,与之同类型的注解还有:
@Deprecated 和 @SuppressWarnings
它们的作用
@Deprecated:声明一个方法是过时被淘汰的,一旦被调用会有Warning警告
(在Eclipse中,被声明的方法会被划线,像这样sayHello() )
例:
@Deprecatedvoid sayHello(){ System.out.print("hello");}
@SuppressWarnings:可以抑制Warning警告
(警告不是报错,程序仍可运行,只是编译器会对一些不好的情况做出高亮提醒,抑制即消除高亮)
例:
@SuppressWarnings("unused") int a=1;
声明了变量不使用的话,编译器是会发出警告的
而我们可以使用@SuppressWarnings(“unused”) 抑制这个警告
(Ps:@SuppressWarnings 需要像 “unused” 这样的参数来告诉编译器
这里需要抑制什么类型的警告
对于@SuppressWarnings,像这样的参数还有很多,我就不展开了,大家可以去查阅相关资料)
(这里只介绍@Target和@Detention 还有很多其他的注解大家就自行学习了)
上面介绍的三个内置注解,是对代码的注解
除此之外,还有对注解的注解
别懵逼,这就解释~
注 解 的 位 置 − − @ T a r g e t \color{red}{注解的位置--@Target} 注解的位置−−@Target
已经给了几个例子,大家可能会发现,注解可以放在很多不同的位置
有对方法的注解,有对变量的注解
其实呢,也可以对类进行注解,还可以放在参数的位置,像这样
String s="say";say(@InsertNew("good") s);
默认情况下,编译器不会限制你放注解的位置
但有时,我们就是要限制这个注解的位置,不让它到处乱放
就需要用到@Target注解了
例:
@Target(ElementType.PARAMETER)@InsertNew
则表示这个注解只能放在参数(parameter)的位置
不然会报错
The annotation @InsertNew is disallowed for this location
Ps:对注解的注解@Target并不是内置注解,需要import
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
常用的参数有:
TYPE----------------对类注解
METHOD-----------对方法注解 PARAMETER-----对参数注解 FIELD---------------对成员变量注解还有等等我就懒得列出来了~
注 解 的 生 命 周 期 − − @ D e t e n t i o n \color{red}{注解的生命周期--@Detention} 注解的生命周期−−@Detention
上面介绍过,我把注解比作是 “便利贴”
原因是它并不是一直依附于代码的,它可以被 “撕下来”
注解有这样一种机制:
把Java代码编译成.class文件时,注解可能会被消除掉
把.class 进JVM时,注解也可能会被设为不可见
这也是定义中说的 “注解不是代码的一部分” 的原因
而决定注解什么时候会被消除的正是对注解的注解
例:
@Retention(RetentionPolicy.RUNTIME)public @interface InsertNew { }
上面的是自定义的注解—InsertNew
@Retention (保留)的作用正是用来声明这个注解的生命周期的
它也像@SuppressWarnings一样需要参数具体说明
不过他的参数种类就少多了
RetentionPolicy.SOURCE(把Java代码编译成.class文件时,注解消除)
RetentionPolicy.CLASS(.class文件进入runtime时,注解不可见) RetentionPolicy.RUNTIME(注解在.class文件进入runtime时可见)
(关于什么是可见,什么是不可见,下面再具体说明)
补充:@Retention 也需要 import
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
这个注解的具体使用还得等介绍了自定义注解(custom annotations)才说得清
马上介绍~
注解带有的信息除了能够和编译器交流外,也可以供程序使用
注解在Java 中其实是一个特殊的接口
例:
当你使用一个自定义注解时
@InsertNew
会报错(因为还没有进行定义)
InsertNew cannot be resolved to a type
IDE还提醒你去创建一个annotation
create annotation InsertNew
然后,你可以直接点也可以自己手动创建
右键–New–annotation
创建好后是这样的
public @interface InsertNew{ }
下 面 介 绍 其 内 部 成 员 \color{red}{下面介绍其内部成员} 下面介绍其内部成员
更确切地说,注解其实是个特殊的接口
public @interface InsertNew { int b=10; int[] a=null; int[] a() default { 1,2,3,4}; String value() default "y"; String sayHello() default "hello world";}
注解可以有其成员变量和方法
在编译器的报错信息里他们被称为 “attribute” 属性
注解中的成员变量的类型是 public static final
注解中的成员方法的类型是public abstract
则有:
注解里的变量是静态变量,可以以类名.变量名 的形式访问
注解里的成员方法是抽象方法,没有函数体实际的例子:
System.out.println(InsertNew.b);
输出:10
而这里的 “方法”,则是相当于一个setAttribute()
要想修改注解里attribute 的值,则同名同类型的 “方法” 不可少------例如上面的int[] a
注 解 缺 省 值 \color{red}{注解缺省值} 注解缺省值
还记得之前例子中的@SuppressWarnings(“unused”) 吗
这其实是缺省的表达
完整的表达是这样的:
@SuppressWarnings(value=“unused”)
当 “value=” 缺省时,参数默认是给value的
而这里的value 对应的就是@SuppressWarnings里的value() 方法
(上面还说过@SuppressWarnings 需要参数不然会报错
原因也就是它里面的value() 方法没有设置default 默认值
对了,这个报错只针对特殊的value(),其他的成员方法可以不设置默认值
Ps:
方法的返回值也有限制(其实是对attribute 类型的限制)
only primitive type(像int,double), String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof
继 续 解 析 @ R e t e n t i o n \color{red}{继续解析@Retention} 继续解析@Retention
上面介绍完自定义注解的定义
可以继续上面@Retention 的说明了
上面简单提及过他的三个参数
但是,在实际应用中,又有什么分别呢
来看例子:
@Retention(RetentionPolicy.RUNTIME)public @interface InsertNew { int b=10; int[] a=null; int[] a() default { 1,2,3,4}; String value() default "y"; String sayHello() default "hello world";}
RUNTIME,也就是运行时
在这个 Retention(保留) 下,程序可以通过Java的反射机制获取注解中的 attribute
例: 如 何 利 用 反 射 获 取 注 解 的 值 \color{red}{如何利用反射获取注解的值} 如何利用反射获取注解的值
public static void main(String[] args) { //down here is the simplest one //deal with the annotation that declared in the head of a class--target is ElementType.TYPE InsertNew column = (InsertNew)Myclass.class.getAnnotation(InsertNew.class); if (column != null) System.out.println(column);//you can get the methods with default value in annotation //if you do not set the method for the attribute you can't see it directly in column else if you use the field way System.out.println(column.b);//you can get attribute with this---field way }
输出结果:
@com.annotation.InsertNew(value=y, a=[1, 2, 3, 4], sayHello=yes, b=11)
10
(com.annotation 是一个package,InsertNew 是自定义注解的类, Myclass是我自定义的一个类)
如果Detention 是CLASS 或SOURCE 的话,则无法看到这一行
@com.annotation.InsertNew(value=y, a=[1, 2, 3, 4], sayHello=yes, b=11)
(因为getAnnotation()方法会返回null))
但是,可以看到10,因为注解中的成员变量的类型是public static final
CLASS 和SOURCE 下,getAnnotation() 方法返回null的原因是不同的
CLASS下获取不到,是因为被设置了 “Invisible” 的属性
(所以尽管.class 文件中存在的注解但是在runtime 仍不可见的原因)
SOURCE下获取不到,是因为根本就没有
下面具体测试看看
使 用 j a v a p 反 编 译 . c l a s s 文 件 \color{red}{使用javap 反编译.class文件} 使用javap反编译.class文件
首先是.java文件
Myclass.java
@InsertNew(sayHello="yes",b=11) //annotation for TYPEpublic class Myclass{ int c=11; public int a=14; //if you do not set you will get the default one //sayHello you set here need a func in annotation //what's more, the implements is not the annotation, you can't use getAnnotation() public void say(String a) { System.out.print("hello"); } }
InsertNew.java
@Retention(RetentionPolicy.RUNTIME)public @interface InsertNew { int b=10; int[] a=null; int[] a() default { 1,2,3,4}; String value() default "y"; String sayHello() default "hello world";}
通过javac 得到.class文件
javac InsertNew.java Myclass.java
I n s e r t N e w 是 注 解 的 j a v a 文 件 , M y c l a s s 是 我 要 添 加 注 解 的 类 \color{blue}{InsertNew是注解的java文件,Myclass是我要添加注解的类} InsertNew是注解的java文件,Myclass是我要添加注解的类
使用javap -v 进行反编译,查看关于注解的情况
javap -v Myclass.class
(我们要研究是的被添加注解的类,所以反编译Myclass.class 就够了)
整个输出的内容有点长,我只展示我们需要的部分
原文:
SourceFile: “Myclass.java”
RuntimeVisibleAnnotations: 0: #20(#21=s#22,#23=I#24)
译:
SourceFile: “Myclass.java”
RuntimeVisibleAnnotations: 0: Lcom/dgut/annotation/InsertNew(sayHello=yes,b=11)
上面的是注解的@Retention为RUNRIME时的情况
下面是@Retention为CLASS的情况:
SourceFile: “Myclass.java”
RuntimeInvisibleAnnotations: 0: Lcom/dgut/annotation/InsertNew(sayHello=yes,b=11)
和RUNTIME 的不同只有一处就在
一个是
RuntimeVisibleAnnotations:
一个是
RuntimeInvisibleAnnotations:
下面再看看@Retention为SOURCE的情况:
SourceFile: “Myclass.java”
没了,连RuntimeInvisibleAnnotations:都没有
原因就是注解在被编译成.class文件时就被消除了
回到定义
注解是元数据,只提供信息,并不带逻辑操作,注解里的方法的是也只是抽象方法,并没有函数体
最后补充一点:注解有多个参数时
@SuppressWarnings({“unchecked”,”unused”})
转载地址:http://aoiyb.baihongyu.com/