JDK注解详解
一、什么是注解
- 注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。
(一)注解的作用 - 不是程序本身,可以对程序作出解释
- 可以被其他程序(比如:编译器等读取)
(二)注解的格式
@注解名
(三)使用范围
注解可以在package、class、method、field等上面,相当于为他们加载了额外的属性信息,我们可以通过反射机制变成实现对这些元数据的访问
二、java中的内置注解
(一)@Override
该注解只适用于定义在方法之上,用于表示该方法重写了父类的同名方法
(二)@Deprecate
此注解可以用于修饰方法,属性,表示不鼓励、不赞成程序员使用这样的元素,通常是因为它很危险或者存在有更好的元素可以选择
(三)SuppressWarnings
用于抑制编译时的警告信息,和前两个注解不同,该注解需要添加一个参数才可以正确的使用
三、元注解 - 元注解即注解的注解,就是对注解的说明解释。Java中定义了4个标准的meta-annotation类型,用于提供对其他annotation类型作说明
- 这些类型和它们所支持的类在java.lang下的annotation包中可以找到
-
@Target:用于描述注解的使用范围
@Retention:表示需要在上面级别保存该注解信息,用于描述注解的生命周期
SOURCE(源码期)<CLASS(编译期)<RUNTIME(运行期)@Document:说明该注解将被包含在javadoc中
-
@Inherited:说明子类可以继承父类中的该注解
四、自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
自定义注解中可根据实际需要定义参数,格式如下:
数据类型 参数名称() ;
注意:
参数的名称后面必须加(),这是固定格式
参数的类型只可以是Class、enum、String
-
可以通过default来申明参数的默认值,否则就需要在使用的时候进行赋值,否则会报错
当设置了默认值后,编译不再报错
注解元素必须要有值,我们定义元素时,通常使用空字符串,0作为默认值
-
如果只有一个参数成员,一般参数名为value(建议)
使用value的话,可以不用注明参数的key
五、注解的内在原理
- 从java源码到class字节码是由编译器完成的,编译器会对java源码进行解析并生成class文件,而注解也是在编译时由编译器进行处理,编译器会对注解符号处理并附加到class结构中,根据jvm规范,class文件结构是严格有序的格式,唯一可以附加信息到class结构中的方式就是保存到class结构的attributes属性中。我们知道对于类、字段、方法,在class结构中都有自己特定的表结构,而且各自都有自己的属性,而对于注解,作用的范围也可以不同,可以作用在类上,也可以作用在字段或方法上,这时编译器会对应将注解信息存放到类、字段、方法自己的属性上。
- 在我们的AnnotationTest类被编译后,在对应的AnnotationTest.class文件中会包含一个RuntimeVisibleAnnotations属性,由于这个注解是作用在类上,所以此属性被添加到类的属性集上。即Test注解的键值对value=test会被记录起来。而当JVM加载AnnotationTest.class文件字节码时,就会将RuntimeVisibleAnnotations属性值保存到AnnotationTest的Class对象中,于是就可以通过AnnotationTest.class.getAnnotation(Test.class)获取到Test注解对象,进而再通过Test注解对象获取到Test里面的属性值。
- 这里可能会有疑问,Test注解对象是什么?其实注解被编译后的本质就是一个继承Annotation接口的接口,所以@Test其实就是“public interface Test extends Annotation”,当我们通过AnnotationTest.class.getAnnotation(Test.class)调用时,JDK会通过动态代理生成一个实现了Test接口的对象,并把将RuntimeVisibleAnnotations属性值设置进此对象中,此对象即为Test注解对象,通过它的value()方法就可以获取到注解值。