@wuxin1994
2019-03-17T12:13:19.000000Z
字数 3921
阅读 843
JAVA
注解(Annotation)
也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
这些标记和注释可以在编译、类加载、运行时被读取,并执行响应的处理。
通俗地讲,注解相当于给类、属性或者方法贴上一个标签。
注解的元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
元注解有五种:
@Retention、@Documented、@Target、@Inherited、@Repeatable
注解通过@interface 关键字定义。
//创建一个名为AnnotationTest的注解。
public @interface AnnotationTest{}
注解的属性类似于类的成员变量。并且注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
可以通过default关键字给出默认值。
举例:
public @interface AnnotationTest{
int id();
String message() default "Hello World";
}
在使用的时候,赋值的方式是在注解的括号内以 value=""形式,多个属性之间用","隔开。
@AnnotationTest(id=0,message="God")
public class Test{
}
//因为属性message有默认值,也可以不赋值
@AnnotationTest(id=1)
public class Test1{
}
//当所有属性都有默认值,括号内为空
甚至当注解没有属性时,可以不用括号:
public @interface Check {
}
//没有属性的注解,不用括号
@Check
public class Test2{
}
注解中所有方法(也就是属性)没有方法体,且只允许public和abstract修饰。缺省默认为public,且注解方法不允许有throws子句。
注解中方法的返回值只能为:基本数据类型,String,Class,美剧类型,注解和他们的一维数组。
前面我们提到,Java中有五种元注解。
@Retention
解释说明一个注解的存活时间。取值如下:
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest {
}
能够将注解中的元素包含到 Javadoc 中去。
@Target
限定注解运用的场景。取值如下:
@Inherited
如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
//因为Test注解被@Inherited注解,所以继承了注解了Test的A类后的B类,也拥有Test这个注解。
public class B extends A {}
Repeatable 是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
@interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。
什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。
我们再看看代码中的相关容器注解。
@interface Persons {
Person[] value();
}
按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。
可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。
Java本身提供了现成的注解。
@Deprecated
用来标记过时的元素。
@Override
提示子类要复写父类中被@Override 修饰的方法
@SuppressWarnings
阻止警告。调用被 @Deprecated 注解的方法后,编译器会警告提醒,而有时候开发者会忽略这种警告,他们可以在调用的地方通过 @SuppressWarnings 达到目的。
@SafeVarargs
参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 的版本中加入的。
@FunctionalInterface
函数式接口注解,这个是 Java 1.8 版本引入的新特性。线程开发中常用的 Runnable 就是一个典型的函数式接口。函数式接口可以很容易转换为 Lambda 表达式。
编译检查 编译器可以利用注解来探测错误和警告信息;通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
编写文档 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。通过代码里标识的元数据生成文档【生成文档doc文档】
代码分析 某些注解可以在程序运行的时候接受代码的提取;通过代码里标识的元数据对代码进行分析【使用反射】
总之,当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。
而注解主要就是给编译器或者APT用的。
注解也就是代码添加元数据,描述信息。
相比使用单独的XML来描述这些元数据,使用注解要简单些,和代码在一起也更好维护。相比使用继承(如TesCase)或者方法前缀的约定(如testXXX是测试方法)要灵活些。
一些插件机制就是通过注解提供插件的元数据,在加载类后扫描所以带该注解的类就可以找到插件,减少了配置的麻烦。
注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。
首先通过Class对象的isAnnotationPresent()方法判断他是否应用了某个注解:
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass){}
然后通过getAnnotation()方法获取Annotation对象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
或者getAnnotations()方法:
public Annotation[] getAnnotations() {}
如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法
@AnnotationTest()
public class Test {
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("id:"+testAnnotation.id());
System.out.println("message:"+testAnnotation.message());
}
}
}