目录
一、为什么要学习注解呢?
二、什么是注解(Annotation)
三、注解有什么作用
四、注解的分类
五、注解基本概念和知识(元注解、Java内置注解、Annotation接口、注解的属性、注解处理器、注解处理类)
六、自定义注解
一、为什么要学习注解呢?
在实际的项目中我们会使用大量的框架,很多框架就会使用大量的注解。学习注解能够使我们读懂别人的代码。我们也可以使用注解,自定义注解使我们的代码更清晰更整洁。从目前行业使用注解的情况,主要是用来简化代码,提高开发效率。
二、什么是注解(Annotation)
从JDK5开始,Java增加了Annotation的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
其实单纯说注解,注解本身没有任何的作用。简单说和注释没啥区别,而它有作用的原因是:注解解释类,也就是相关对代码进行解释的特定类。一般这些类使用反射是可以拿到的。因此,通过注解和反射这两者的结合使用,是可以做到很多功能的。
-
功能:注解是一种“增强型”的注释。只不过相对于只能给人看的注释,注解可以给电脑(JVM,程序等)看。
-
原理:注解的底层是Annotation接口的继承者。只不过相对于日常使用的接口,注解需要使用@interface,但是编译的结果依旧是接口继承(如TestAnnotation extend Annotation)。
三、注解有什么作用
理解了注解之后,大概也可以知道注解有什么用处了,这是我个人理解的和结合目前大家对注解的使用的总结出来的:
-
注释:起到了注释的作用,也就可以提高代码的可读性了。
-
编写文档:通过代码里标识的元数据生成文档【生成doc文档】
-
代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
-
编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
四、注解的分类
1.按运行机制分
- 源码注解:只在源码中存在
- 编译时注解:在class中依然存在,如@Deprecated
- 运行时注解:运行阶段起作用,甚至会影响运行逻辑的注解 如@Autowired
2.按来源分
- JDK注解,Java预置的本身就支持的注解
- 自定义注解(三方sdk的注解,这里也包含在自定义注解里面)
五、一些基本概念知识(元注解、Java内置注解、Annotation接口、注解的属性、注解处理器、注解处理类)
1.元注解
元注解就是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。目前我知道的元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
- @Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。它的取值如下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
如果注解类型声明上没有@Retention声明,则保留策略默认为RetentionPolicy.CLASS。
-
@Documented
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。定义 Annotation 时,@Documented 可有可无;若没有定义,则 Annotation 不会出现在 javadoc 中。
javadoc规范参考:https://blog.csdn.net/linton1/article/details/93733508 -
@Target
Target 是目标的意思,@Target 指定了注解运用的地方。@Target 有下面的取值
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
定义 Annotation 时,@Target 可有可无。若有 @Target,则该 Annotation 只能用于它所指定的地方;若没有 @Target,则该 Annotation 可以用于任何地方。
-
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。 -
@Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
2.内置的普通注解
Java有内置一些注解,最早内置了三种注解,后面也添加有一些新的注解,请读者自行了解:
- @Override:检查该方法是否是重载方法;如果父类或实现的接口中,如果没有该方法,编译会报错。
- @Deprecated:已经过时的方法;如果使用该方法,会有警告提醒。
- @SuppressWarnings:忽略警告;比如使用了一个过时的方法会有警告提醒,可以为调用的方法增加 @SuppressWarnings 注解,这样编译器不在产生警告。
3.Annotation接口(注解的本质)
「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』。
The common interface extended by all annotation types
所有的注解类型都继承自这个普通的接口(Annotation)
这句话有点抽象,但却说出了注解的本质。我们看一个 JDK 内置注解的定义:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这是注解 @Override 的定义,其实它本质上就是:
public interface Override extends Annotation{
}
没错,注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。
4.注解的属性
注解的属性也叫做成员变量,注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
Java中注解成员的类型必须是如下几类:
- 基本数据类型(boolean, byte, char, short, int, long, float, double等);
- String;
- Class;
- 枚举;
- 其他的注解;
- 以上类型的数组;
注解属性的语法形式如下:
[访问级别修饰符] [数据类型] 名称() default 默认值;
例如,我们要定义在注解中定义一个名为 value 的字符串属性,其默认值为空字符串,访问级别为默认级别,那么应该定义如下:
String value() default "";
注意:在注解中,我们定义属性时,属性名后面需要加 ()
5.注解处理器(Annotation Processing Tool,简称APT)
根据元注解@Retention指定值的不同,注解可分为SOURCE、CLASS和RUNTIME三种类型。当被声明为SOURCE时,注解仅仅在源码级别被保留,编译时被丢弃;声明为CLASS时,注解会由编译器记录在class文件内,但在运行时会被忽略,默认的Retention级别即为CLASS;声明为RUNTIME时,注解将被保留到运行时,可通过反射在运行时获取到。针对CLASS类型的注解,就需要用到注解处理器。注解处理器(Annotation Processing Tool)是javac内置的工具,用于在编译时期扫描和处理注解信息。从JDK 6开始,apt暴露了可用的API。一个特定的处理器接收一个Java源代码或已编译的字节码作为输入,然后输出一些文件(通常是.java文件)。这就意味着你可以使用apt动态生成代码逻辑,需要注意的是apt仅可以生成新的Java类而不能对已存在的Java类进行修改。所有生成的Java类将和其他源代码一起被javac编译。
6.注解处理类
注解处理类就是帮助程序员快速的构造自定义注解处理器的一些类和接口。Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。
AnnotatedElement接口实现类
- AccessibleObject(可访问对象,如:方法、构造器、属性等)
- Class(类,就是你用Java语言编程时每天都要写的那个东西)
- Constructor(构造器,类的构造方法的类型)
- Executable(可执行的,如构造器和方法)
- Field(属性,类中属性的类型)
- Method(方法,类中方法的类型)
- Package(包,你每天都在声明的包的类型)
- Parameter(参数,主要指方法或函数的参数,其实是这些参数的类型)
AnnotatedElement接口方法
1.default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注解出现在当前元素上,则返回true,否则将返回false。这种方法主要是为了方便地访问一些已知的注解。
2.<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果在当前元素上存在参数所指定类型(annotationClass)的注解,则返回对应的注解,否则将返回null。
3.Annotation[] getAnnotations()
返回在这个元素上的所有注解。如果该元素没有注释,则返回值是长度为0的数组。该方法的调用者可以自由地修改返回的数组;它不会对返回给其他调用者的数组产生影响。
4.default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
如果参数中所指定类型的注解是直接存在于当前元素上的,则返回对应的注解,否则将返回null。这个方法忽略了继承的注解。(如果没有直接在此元素上显示注释,则返回null。)
5.default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
返回与该元素相关联的注解。如果没有与此元素相关联的注解,则返回值是长度为0的数组。这个方法与getAnnotation(Class)的区别在于,该方法检测其参数是否为可重复的注解类型(JLS 9.6),如果是,则尝试通过“looking through”容器注解来查找该类型的一个或多个注解。该方法的调用者可以自由地修改返回的数组;它不会对返回给其他调用者的数组产生影响。参考@Repeatable。
六、自定义注解
1.注解的定义
使用 @interface 来声明一个注解,注解的语法格式如下:
public @interface 注解名 {定义体}
我们来定义一个注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{}
2.自定义Java注解处理器
实现一个自定义注解处理器需要有两个步骤,第一是实现Processor接口处理注解,第二是注册注解处理器。
实现Processor接口
通过实现Processor接口可以自定义注解处理器,这里我们采用更简单的方法通过继承AbstractProcessor类实现自定义注解处理器。实现抽象方法process处理我们想要的功能。
public class CustomProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
return false;
}
}
一些具体实现可以参考自定义Java注解处理器,这里不多叙述
注册注解处理器
使用Google提供的@AutoSerivce注解: 引入依赖:
dependencies { compile 'com.google.auto.service:auto-service:1.0-rc2' }
使用@AutoService生成META-INF/services/javax.annotation.processing.Processor文件:
AutoService(Processor.class) public class AutoBuilderProcessor extends AbstractProcessor { ...
}
3.自定义注解实战(这里找的都是一些Android实战案例,对于纯Java的小伙伴只能表示抱歉了)
对于自定义注解,大家可以去学习使用 ButterKnife和Dagger2 框架,并理解他们的原理,这样可以帮助大家加深对自定义注解的使用。下面是一些实战案例:
仿ButterKnife,实现自己的BindView
Android 注解(Annotation)的入门与使用(一)
手写ButterKnife来搞明白Android注解处理器
参考:
1.深入理解 Java 注解
2.Java注解处理器
3.AnnotatedElement
4.自定义Java注解处理器
网友评论