Java 基础之注解
文章目录
概览
注解(Annotation),是从 Java 5 开始引入的一项新特性。所谓注解,就是一种元数据,可以藉此为程序添加非功能性的数据。注解对它们所标注代码的执行不会造成影响。
尽管注解对代码的执行不会影响代码的执行,但注解信息却可以嵌入编译生成的类文件,甚至在运行时得以保留。在 Java 源码中使用注释和 Javadoc 的标签也可以为程序添加一些描述信息,然而注解和注释信息却远不相同。
注解在 Java 开发,尤其是类库和框架开发中有很多应用。通常,注解的使用有以下三种方式:
- 为编译器提供信息:可以被编译器用于错误检查和抑制警告
- 编译和部署时进行处理:通过注解信息生成代码、配置文件等
- 运行时进行处理:一些注解信息可以保留到运行时,通过反射进行获取
注解基础用法
注解的最简单形式就是使用 @Entity
的形式,用 @ 符号告知编译器紧跟着的是注解。比如我们最常使用的 @Override:
|
|
注解中可以包含元素,并可以为元素赋值:
|
|
在含有多个元素的情况时,用 ,
进行分割:
|
|
如果只有一个名为 value 的元素,可以忽略名称:
|
|
可以同时叠加使用多个注解:
|
|
在 Java 8 之前,注解只能被使用在声明中:类、成员、方法以及其它编程元素的声明。在 Java 8 中,引入了类型注解 (type anotation) 的新特性,注解可以被应用于使用声明的任何地方,下面给几个例子:
类实例创建:
|
|
类型转换:
|
|
实现接口:
|
|
异常:
1 2 |
void monitorTemperature() throws @Critical TemperatureException { ... } |
内置注解
Java 语言中内置了一些基本的注解,大致可以分为两类,一类通常用于 Java 代码中,供编译器使用;还有一类用来对注解进行注解,也称为元注解。
基本注解
Java 5 中包括三个基本注解,分别是 @Deprecated , @Override 和 @SuppressWarnings。
@Deprecated 表明被注解的元素已经被弃用,不应该在后续的代码中使用。通常,用 @Deprecated 注解元素的时候也会搭配 Javadoc 的 @deprecated 标签一起使用,从而在文档中进行说明。当程序中使用了被 @Deprecated 注解的类、方法或成员时,编译器会给出警告信息。
@Override 告知编译器被注解的元素覆盖了父类中的元素。@Override 不是必须的,但是使用时可以用来检查错误。如果用 @Override 注解的方法没有正确重写父类的方法(比如父类修改了相应的方法签名),编译器会给出错误信息。
@SuppressWarnings 告知编译器抑制特定的警告信息。例如,如果使用了一个被弃用的方法,可以用下面的方式消除编译时的警告信息:
|
|
如果要抑制多类警告信息,可以这样使用:
|
|
@SafeVarargs 注解是 Java 7 开始引入的。当应用于方法或构造方法中时,假定代码不会在可变参数 (varargs ) 中执行不安全的操作。使用这个注解时,和可变参数相关的 unchecked 警告会被抑制:
|
|
@FunctionalInterface 注解是 Java 8 中引入的,表明被注解的接口是一个函数式接口。
元注解
元注解用于给注解添加注解,通常在自定义注解时会用到。Java 中定义了 5 个元注解,下面进行简单的介绍:
@Retention,指定注解的保留范围,可以将注解限定在源码级,也可以保留到运行时,可以有三个可用的值,在枚举 RetentionPolicy 中定义:
- RetentionPolicy.SOURCE ,被标记的注解只会在源码级存在,会被编译器忽略
- RetentionPolicy.CLASS ,被标记的注解会被编译器保留到 class 文件中,但会被 JVM 忽略(默认)
- RetentionPolicy.RUNTIME ,被标记的注解会被 JVM 保留,并且可以在运行时使用(通过反射)
@Target,限定注解可以修饰的元素的类型,可用的值在 ElementType 中定义:
- ElementType.ANNOTATION_TYPE ,可以被应用于注解类型
- ElementType.CONSTRUCTOR ,构造方法
- ElementType.FIELD ,成员
- ElementType.LOCAL_VARIABLE ,局部变量
- ElementType.METHOD ,方法
- ElementType.PACKAGE ,包
- ElementType.PARAMETER ,方法参数
- ElementType.TYPE ,任意类型
- ElementType.TYPE_PARAMETER, Java 8 新增,用于类型注解
- ElementType.TYPE_USE, Java 8 新增,用于类型注解
@Inherited,表明注解可以从父类继承。默认情况下注解是不可继承的。
@Documented,表明注解在被应用于元素时,这些元素应该使用 Javadoc 生成文档。默认不使用。
@Repeatable,自 Java 8 开始引入,表明注解可以在一个元素上重复使用。
自定义注解
在对元注解有所了解后,我们就可以自定义注解了。自定义注解使用 @interface 关键字进行,类似于接口的声明。
|
|
可以为注解元素添加默认值:
|
|
注解元素类似于方法,有类型和名称,其类型可以是: - 基本数据类型 - 字符串 - 类 - 注解 - 枚举 - 所有以上类型的数组
在使用注解时,我们需要为所有的注解元素设置值,有默认值的例外。
如果想让一个类和它的子类都包含某个注解,就可以使用 @Inherited 来修饰这个注解。
实际上注解就是一个特殊的接口,隐式地扩展了 java.lang.annotation.Annotation 接口,可以从反编译后的代码中一窥究竟:
|
|
反编译 class 文件:
|
|
注解处理
注解在很多框架和类库中都有使用,可以大幅度精简配置文件。对注解信息通常有两种处理方式,分别在运行时和编译期间进行处理。
如果注解信息被保留到了运行时,那么可以通过反射的方式来获取注解中的信息。在 java.lang.reflect 包中包含处理注解信息的相关类和接口。其中,AnnotatedElement
接口是获取注解信息的重要接口,一些反射类,如 Class
, Constructor
, Field
, Method
和 Package
都实现了该接口,通过该接口的 getAnnotations()
等方法可以获取被注解元素的注解,进而获得注解中的信息。
在运行时通过反射动态获取注解信息会对效率有一定的影响,另一种处理注解的方式是在编译期间进行处理。在 Java 源码被编译时,可以通过注解处理器 (annotation processors) 进行处理。注解处理器在编译时扫描代码中的注解信息,动态地生成文件或创建新的代码。在编译过程中,这一过程会持续多次,直到没有新的文件或代码生成。对注解处理器感兴趣的话可以看一下这篇文章。
小结
文中对 Java 注解的基础知识进行了介绍。注解是一种特殊的接口,可以为被注解的元素(如类,方法等)提供额外的信息,但并不影响代码的执行。通过在编译器或运行时对注解进行处理,可以利用注解信息完成一些强大的功能,在很多框架中都有所体现。
-EOF-
参考
The Java™ Tutorials Java深度历险(六)——Java注解 Java 技术手册