java(16):Java Annotation(Java 注解),
java(16):Java Annotation(Java 注解),
目标:掌握 Java Annotation 特性与使用方式
Annotation 工作方式:
从 Java5.0 版本发布以来,5.0 平台提供了一个正式的 annotation(注解) 功能:允许开发者定义、使用自己的 annotation 类型。此功能由一个定义 annotation 类型的语法和一个描述 annotation 声明的语法、读取 annotation 的 API、一个使用 annotation 修饰的 class 文件以及一个 annotation 处理工具(apt)组成。
annotation 并不直接影响代码语义,它工作的方式类似程序的工具或者类库一样,可以反过来对正在运行的程序语义有所影响。Annotation 在相应的情况下(下面会说什么样的情况下),可以从源文件、class 文件或者以在运行时反射的多种方式被读取。
JDK 提供的常见注解:
a. Override 注解表示子类要重写(override)父类的对应方法。java.lang.Override 是个 Marker annotation,用于标识的注解,该注解名称本身即表示了要给工具程序提供的信息。(也就是编译器看到了注解的名称,就知道什么意思了)
b. Deprecated 注解表示方法是不建议被使用的。对编译程序说明某个方法已经不建议使用,即该方法是过时的。java.lang.Deprecated 也是个 Marker annotation,Deprecated 这个名称在告知编译程序,被 @Deprecated 标识的方法是一个不建议被使用的方法。
c. SuppressWarnings(xxx) 注解表示抑制警告。对编译程序说明某个方法中若有警告讯息,则加以抑制。
自定义 Annotation 类型:
1.
关于 Marker Annotation 的定义,也就是 Annotation 名称本身即提供信息。对于程序分析工具来说,主要检查是否有 Marker Annotation 的出现,并作出对应的动作。
2.
当注解中的属性名为 value 时,在对其赋值时可以不指定属性的名称而直接写上属性值即可;除了 value 以外的其它值都需要使用 name = value 这种赋值方式,即明确指定给谁赋值,否则报错。
eg:
JDK自定的 SuppressWarnings 注解两种写法:
@SuppressWarnings("unchecked")
@SuppressWarnings(value = "unchecked")
3.
我们自定义 Annotation 类型时,注解类型关键字为 @interface。value 成员可以设定默认值,用“default”关键词,并且可以使用数组以及枚举类型等,使用时,多个成员值之间使用逗号隔开。
eg:
package com.cfm.annotation;
public @interface AnnotationTest {
String value1() default "ym";
String[] value2();
EnumTest value3();
}
enum EnumTest{
YOU, ME, HE
}
4.
我们使用 @interface 关键字自定义 Annotation 类型时,实际上是自动继承了 java.lang.annotation.Annotation 接口,由编译器自动为我们完成了其它产生的细节。注意:我们在自定义 Annotation 类型时,不能继承其它的 Annotation 类型或是接口。
5.
定义 Annotation 类型时,我们也可以使用包来管理类别,方式类似于类的导入功能。
Annotation 高级特性:
1. 告知编译程序如何处理 @Retention
java.lang.annotation.Retention 类型可以在您定义 Annotation 类型时,指示编译程序该如何对待您的自定义的 Annotation 类型。缺省(即系统默认状态,意思与默认相同)编译程序会将 Annotation 信息留在 .class 档案中,但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息。
在使用 Retention 类型时,需要提供 java.lang.annotation.RetentionPolicy 的枚举类型。
// RetentionPolicy 类型源码
public enum RetentionPolicy {
/**
* 编译程序不会将 Annotation 储存于 class 文件中,编译程序处理完 Annotation 信息后就完成任务
*/
SOURCE,
/**
* 编译程序将 Annotation 储存于 class 文件中,缺省(默认值),但是不能被 JVM 通过反射获取到。
*/
CLASS,
/**
* 编译程序将 Annotation 储存于 class 文件中,可由 JVM 通过反射获取到。
*/
RUNTIME
}
2.
RetentionPolicy 为 SOURCE 的例子:@SuppressWarnings,这个注解在编译期间告知编译器来抑制警告,所以不必将这个信息存储到 .class 文件中。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
...
}
3.
RetentionPolicy 为 RUNTIME 的时机,可以像是您使用 Java 设计一个程序代码分析工具,您必须让 VM 能读出 Annotation 信息,以便在分析程序时使用,搭配反射(Reflection)机制,就可以达到这个目的。
4.
java.lang.reflect.AnnotatedElement 接口:
// 源码
public interface AnnotatedElement {
/**
* 判断是否包含参数中的注解类型
*/
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
}
/**
* 获取参数中的注解类型
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* 获取该方法或类等所有的包含 @Retention(RetentionPolicy.RUNTIME) 类型的注解。因为只有这种注解才能通过反射机制读取
*/
Annotation[] getAnnotations();
/**
* 获取与参数注解相关联的注解类型
*/
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
}
/**
* 如果存在参数中指定类型的注解类型,则返回该元素的注解,否则为空。
*/
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
}
/**
* 如果指定类型的注解直接存在或间接存在,则返回该元素的注解类型。
*/
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
}
/**
* 类似 getAnnotations()
*/
Annotation[] getDeclaredAnnotations();
}
Class、Constructor、Field、Method、Package等类都实现了 AnnotatedElement 接口,通过此接口提供的方法,我们可以通过反射机制获取相应的注解信息。
5.
我们在自定义 Annotation 类型时,必须设定 RetentionPolicy 为 RUNTIME,才能通过反射使用上面 AnnotatedElement 接口提供的相关方法获取相关的 Annotation 信息。
eg:
// 我们自定义的注解类型
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value1() default "ym";
}
// 该类中使用我们上面自定义的注解类型
@MyAnnotation(value1 = "cfm2")
public class MyTest {
@MyAnnotation(value1 = "cfm2")
@Deprecated
@SuppressWarnings("unchecked")
public void test(){
System.out.println("my test");
}
}
// 通过 java 反射机制,使用 java 提供的相关反射类注解相关的 api
public class MyReflection {
public static void main(String[] args) throws Exception {
MyTest myTest = new MyTest();
Class<MyTest> c = MyTest.class;
Method method = c.getDeclaredMethod("test", new Class[]{});
// 判断这个方法上面有没有 MyAnnotation 这个注解类型,如果有就执行这个方法
if(method.isAnnotationPresent(MyAnnotation.class)){
method.invoke(myTest, new Object[]{});
// 返回这个注解类型
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation);
// 看看这个方法上面注解的值
String value = myAnnotation.value1();
System.out.println(value);
// 返回这个方法的所有 @Retention(RetentionPolicy.RUNTIME) 类型注解
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations){
System.out.println(annotation);
}
}
}
}
6.
限定 Annotation 使用的目标 @Target。使用 java.lang.annotation.Target 可以定义该注解类型修饰的目标。在定义的时候要指定 java.lang.annotation.ElementType 的枚举值之一。
ElementType 枚举类型:
7.
要求为 API 文件 @Documented。想要在使用者制作 JavaDoc 文件的同时也一并将 Annotation 的信息加入至 API 文件中,使用 java.lang.annotation.Documented。
8.
缺省父类别中的 Annotation 并不会被继承至子类别中,可以在定义的 Annotation 类型加上 java.lang.annotation.Inherited 类型的 Annotation,以让父类别中的 Annotation 继承到子类别中。
相关文章
- 暂无相关文章
用户点评