Annotation翻译过来为注解、释文等。而在Java中注解算是一个尤为重要的知识点,而且不易理解。
摘抄至百度百科:
java.lang.annotation,接口 Annotation。对于Annotation,是Java5的新特性,JDK5引入了Metadata(元数据)很容易的就能够调用Annotations。Annotations提供一些本来不属于程序的数据,比如:一段代码的作者或者告诉编译器禁止一些特殊的错误。An annotation 对代码的执行没有什么影响。Annotations使用@annotation的形式应用于代码:类(class),属性(attribute),方法(method)等等。一个Annotation出现在上面提到的开始位置,而且一般只有一行,也可以包含有任意的参数。
是不是感觉看完还是一脸懵逼完全不知道说的是什么?
别担心:我通俗的把Annotation理解为一个Label(标签),用于对其他事物进行修饰解释。
注解的语法以及使用
- 定义:注解通过
@interface
关键字进行定义。
下面即定义了一个名为TestAnnotation的注解
public @interface TestAnnotation{}
- 应用:在使用的地方加上
@TestAnnotation
即可
@TestAnnotation
public class Test{}
要想注解能够正常使用,还需要介绍一下元注解。
元注解
什么元注解?就是可以注解到注解上的注解
。即元注解是基本的注解,他能到应用到其他注解上,去修饰其他的注解。
基本的元注解有如下:
元注解 | 可以取的值 | 所取值的含义 | 意义 |
---|---|---|---|
@Retention | RetentionPolicy.SOURCE | 只保留在源码阶段,编译时丢弃 | 生命周期,即根据它来判断注解的存活时长 |
RetentionPolicy.CLASS | 保留到编译进行时,不加载到JVM中去 | ||
RetentionPolicy.RUNTIME | 保留到程序运行时并加载到JVM中去,在程序运行时可以加载到它们 | ||
@Documented | 将注解的元素包含到javadoc文档中去 | ||
@Target | ElementType.ANNOTATION_TYPE | 可以给注解进行注解 | 指定注解应用的目标(即使用场景) |
ElementType.CONSTRUCTOR | 可以给构造方法进行注解 | ||
ElementType.FIELD | 可以给属性进行注解 | ||
ElementType.LOCAL_VARIABLE | 可以给局部变量进行注解 | ||
ElementType.METHOD | 可以给方法进行注解 | ||
ElementType.PACKAGE | 可以给包进行注解 | ||
ElementType.PARAMETER | 可以给方法内的参数进行注解 | ||
ElementType.TYPE | 可以给给一个类型进行注解,类/接口/枚举等 | ||
@Inherited | 继承的意思,它并不是说注解本身可以被继承,而是说一个超类被@Inherited注解过的注解进行注解时,如果它的子类没有被其他注解所注解,那么子类就继承了超类的注解 | ||
@Repeatable | 可重复的意思。什么样的注解会多次应用呢?通常是注解的值可以同时取多个。 |
元注解的使用例子:
@Retetion:
@Retetion(RetetionPolicy.RUNTIME)
public @interface TestAnnotation{}
@Inherited:
@Inherited
@Retetion(RetntionPolicy.RUNTIME)
public @interface TestAnnotation{}
@TestAnnotation
class Parent{}
class Children extend Parent{}
解释:
注解 TestAnnotation被 @Inherited 修饰,之后类 Parent 被 TestAnnotation 注解,类 Children 继承 Parent,类 Children 也拥有 Test 这个注解。
@Repeatable:
例子:一个人可以是产品也可以是程序员还可以是驻唱
@interface Persons{
Person[] value();
}
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
@Person(role="singer") //给注解的role属性赋值为singer
@Person(role="PM")
@Person(role="coder")
public class Man{}
解释:
@Repeatable注解了Person。后面括号的类相当于注解容器(可以存放其他注解的注解),按照规定内部必须有一个value的属性,属性类型是一个被@Repeatable注解过的注解数组。 即Persons是一个总标签,上面贴满各种各样的Person标签。
@Target:
@Target(ElementType.TYPE)
@Retetion(RetntionPolicy.RUNTIME)
public @interface TestAnnotation{}
注解属性
定义注意点:
- 注解的属性也叫成员变量,无方法。
- 注解的属性以“无形参的方法”的形式来声明
- 方法名定义了该成员变量的名字,返回类型为成员变量的类型
- 注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。
使用案例:(内部注释已经很完善了)
定义一个注解
@Target(ElementType.TYPE)
// 运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id(); // 定义一个属性名为id 的int变量
String msg(); // 定义一个属性名为msg 的String变量
double price() default 2.0f; //定义一个默认值为2.0名为price的Double变量
}
使用注意点:
- 使用,括号内给变量赋值
- 赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开。
- 注解中属性可以有默认值,默认值需要用 default 关键字指定;有默认值在使用时就无需进行赋值
- 当注解中只有一个value属性时,在使用时可以直接在括号里加上值,无需value=""
- 注解没有任何属性时,使用 括号也可以省略
使用上面的注解:
@TestAnnotation(id = 1, msg = "wdl") //定义默认值的可有可无
class Test {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@interface TextAnnotation{
int value();
}
// @TextAnnotation(value = 2)
@TextAnnotation(2)
class Text {
}
@Retention(RetentionPolicy.CLASS)
@interface Perform{
}
Java中预置的注解
-
@Deprecated
标记过时的元素,编译器在编译阶段遇到这个注解时会发出提醒警告 -
@Override
复写父类中的被@Override修饰的方法 -
@SuppressWarnings
阻止警告,@Deprecated标记过的方法会发出警告,我们可以使用这个注解忽略警告 -
@SafeVarargs
参数安全类型注解。提醒开发者不要用参数做一些不安全的操作 -
@FunctionalInterface
函数式接口注解。Runnable就是一个被@FunctionalInterface修饰的接口。函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。
上面大概的介绍了一下注解的基本使用方法以及常用的预置注解/元注解等。前面提到过注解用来修饰/解释,那我们让这些注解生效应该怎么办,即提取这些注解?那就是使用反射技术了。这一过程就相当于撕标签/检阅标签内容了。
注解提取的一般步骤
- 通过xx.class内置的函数判断这个类是否应用了某个注解
/**
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}
- 通过
getAnnotation()
或者getAnnotations()
方法来获取 Annotation 对象。
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@SuppressWarnings("unchecked")
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
/**
* @since 1.5
*/
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
- 调用它们的属性方法
简单的使用例子:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Check{
int value();
}
// 注解没有任何属性时,使用 括号也可以省略
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Perform{
}
// 使用,括号内给变量赋值
// 赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开。
// 注解中属性可以有默认值,默认值需要用 default 关键字指定;有默认值在使用时就无需进行赋值
// 注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。
@TestAnnotation(id = 1, msg = "wdl")
class Test {
@Check(6)
int a;
@Perform
public void testMethod(){
}
}
boolean isAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if (isAnnotation){
TestAnnotation textAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println(textAnnotation.id());
System.out.println(textAnnotation.msg());
System.out.println(textAnnotation.price());
}
try {
Field field = Test.class.getDeclaredField("a");
field.setAccessible(true);
if (field!=null){
Check check = field.getAnnotation(Check.class);
System.out.println("Check value is:"+ check.value());
}
Method testMethod = Test.class.getDeclaredMethod("testMethod");
if (testMethod!=null){
Annotation[] annotations = testMethod.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
System.out.println("testMethod annotations:"+annotations[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
输出为:
注解使用场景
大名鼎鼎的Butterknife/Retrofit/Dagger
等都是使用注解来实现的。因此注解非常的重要虽然平常开发中很少遇到。
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
作用:
- 提供信息给编译器:编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理:软件工具可以利用注解信息来生成代码,HTML文档等
- 运行时处理:某些注解可以在程序运行时接受代码的提取
当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation
不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为
APT(Annotation Processing Tool)。
学完终于知道了使用Butterknife时多加的那一串是什么意思了。。。。
参考自:
https://blog.youkuaiyun.com/briblue/article/details/73824058