1. Annotation定义规范
注解类似一个标签,作为元数据来描述Java代码。
注解通过 @interface 关键字进行定义。注解的定义类似接口定义,只是比接口定义多了个@。
public @interface TestAnnotation{
}
@TestAnnotation
public class Test {
}
不过想要注解正常工作,还需要理解下元注解
2. 元注解
元注解是最基本的注解,用于描述和解释其他注解。元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable
2.1 @Retention注解
Retention是保留期的意思,表示该注解的存活有效期。
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
2.2 @Target注解
目标,用于标注该注解用于什么地方。比如只能用于方法、属性、方法参数等。
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
2.3 @Inherited注解
继承的含义,比如注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。可以理解为父亲有钱,那么儿子也是富豪。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {} // 类B默认也拥有Test注解
2.4 @Repeatable注解
可重复的,表示注解的值可以取多个。比如,一个人他既是程序员又是产品经理,同时他还是个画家。
@interface Persons {
Person[] value(); // 属性类型是一个被 @Repeatable 注解过的注解数组
}
@Repeatable(Persons.class) // 括号中的相当于容器注解,用来存放其他注解
@interface Person{
String role() default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
通俗来说,Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。
2.5 @Documented注解
注解包含的元素可以被包含至Javadoc中
3 注解的属性
注解的属性也就是成员变量。并且注解只有成员变量,没有方法。定义注解的属性以“无参的方法”形式声明。方法名字表明了属性的名称,返回值表明了成员变量的类型。同时注意:类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。注解中属性可以有默认值,默认值需要用 default 关键值指定。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg() default "Hi"; // 带默认值的属性
}
4 注解的提取和使用
注解的提取需要使用反射。
@TestAnnotation(msg="hello")
public class Test {
@Check(value="hi")
int a;
@Perform
public void testMethod(){}
@SuppressWarnings("deprecation")
public void test1(){
Hero hero = new Hero();
hero.say();
hero.speak();
}
public static void main(String[] args) {
// 使用Class对象的 isAnnotationPresent方法判断是否有某个注解
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
// 获取 Annotation对象
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
// 获取类的注解
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
try {
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
// 获取一个成员变量上的注解
Check check = a.getAnnotation(Check.class);
if ( check != null ) {
System.out.println("check value:"+check.value());
}
Method testMethod = Test.class.getDeclaredMethod("testMethod");
if ( testMethod != null ) {
// 获取方法中的所有注解
Annotation[] ans = testMethod.getAnnotations();
for( int i = 0;i < ans.length;i++) {
System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println(e.getMessage());
} catch (SecurityException e) {
e.printStackTrace();
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
5 总结
1. 注解的创建如同创建了个接口,但是多了个@
2. 用于定义注解的元注解(合适失效、应用于什么地方、是否可继承和重复)
3. 注解的属性(定义的规范格式、属性的类型值)
4. 注解的提取需要使用反射,反射比较慢,因此使用是也需要注意。