注解概述
在 Java 中,注解(Annotation)引入始于 Java5,用来描述 Java 代码的元信息,通常情况下注解不会直接影响代码的执行,尽管我们使用一些特定类型的注解也可以达到这一目的。
Java Annotation Purposes
Java 注解通常用来达到以下的目的:
- Compiler instruction :编译器说明
- Build-time instruction:构建时说明
- Runtime instruction:运行时说明
Java 已经内置了三种 Compiler instruction,见后文。
Java 注解可以应用在构建项目时。构建过程包括生成源码,编译源码,生成 xml 文件,打包编译的源码和文件到 jar 包等。软件的构建通常使用诸如 Apache Ant 和 Maven 这种工具自动完成(Android 中使用 Gradle 脚本语言)。这些构建工具会依照特定的注解扫描 Java 代码,然后根据这些注解生成源码或者文件。
通常情况下,注解并不会出现在编译后的代码中,但是如果想要出现也是可以的。Java 支持运行时的注解,使用 Java 的反射我们可以访问 到这些注解,运行时的注解的目的通常是提供给程序和第三方 API 的一些指令。
注解基础
一个简单的 Java 注解类似于@Entiry
。其中@
的意思就是告诉编译器这是一个注解。而Entity
则是注解的名字。通常在文件中写法如下:
1 | public Entity{ |
注解元素
Java 注解可以使用元素来进行设置一些值,注解中的元素类似于属性或者参数。定义包含元素的注解示例代码:
1 | public @interface Entity{ |
使用包含元素的注解示例代码:
1 | (tableName = “vehicles”) |
上述注解的元素名称称为tableName
,设置的值为vehicles
。没有元素的注解不需要使用括号。
如果包含多个元素,使用方法如下:
1 | "id") (tableName=“vehicles”,primaryKey= |
如果注解只有一个元素,通常我们的写法是这样子的:
1 |
但是这种情况下,当且仅当元素名为value
,我们也可以简写,即不需要填写元素名value
,效果如下:
1 |
注解使用
注解可以用来修饰代码中的这些元素
- 类
- 接口
- 方法
- 方法参数
- 属性
- 局部变量
举个栗子:
1 | //类注解 |
Java 内置编译器说明注解
Java 中有三种内置注解,这些注解用来为编译器提供说明。
@Deprecated
表示被注解的类,方法,属性属于过时的或者被弃用的状态;@Override
表示需要重写方法@SuppressWarnings
表示不提示警告信息
创建自己的注解
在 Java 中,我们可以创建自己的注解,注解和类、接口文件一样定义在自己的文件里面。如下:
1 | public MyAnnotation { |
自定义注解跟接口定义方法类似,都有类型和名称。这些类型可以是一下几种:
- 原始数据类型
- String
- class
- annotation
- 枚举
- 一维数组
如何使用:
1 |
|
** 注意:我们需要为所有的注解元素设置值,一个都不能少。除非设置了默认的元素值。**
注解元素默认值
对于注解中的元素,我们可以为其设置默认值,设置方式为在元素的后面添加default “”
关键字+默认值。如下:
1 | public MyAnnotation { |
当我们使用带默认值的注解时,可以不用为有默认值的注解元素赋值,即让其使用默认值作为值。如下:
1 |
|
@Retention
@Retention
是用来修饰注解的注解,使用这个注解,我们可以做到:
- 控制注解是否写入 class 文件;
- 控制 class 文件中的注解是否运行时可见。
控制很简单,其中包括三种策略:
RetentionPolicy.SOURCE
表明注解仅存在源码之中,不存在.class 文件中,更不能运行时可见。常见的注解为@Override
,@SuppressWarnings
.RetentionPolicy.CLASS
这是默认的注解保留策略。这种策略下,注解将存在于.class 文件中,但是不能被运行时访问。通常这种注解策略用来处理一些字节码级别的操作。RetentionPolicy.RUNTIME
这种策略下可以被运行时访问到。通常情况下,我们都会结合反射来做一些事情。
使用示例:
1 |
|
@Target
使用@Target 注解,我们可以设定自定义注解可以修饰那些 java 元素。示例:
1 |
|
上面的代码说明@MyAnnotation 注解只能修饰方法。@Target()
参数值有:
- ElementType.ANNOTATION_TYPE(注:修饰注解)
- ElementType.CONSTRUCTOR //修饰构造方法
- ElementType.FIELD //修饰属性
- ElementType.LOCAL_VARIABLE//修饰变量
- ElementType.METHOD//修饰方法
- ElementType.PACKAGE//修饰包
- ElementType.PARAMETER//修饰参数
- ElementType.TYPE(注:任何类型,即上面的的类型都可以修饰)
@Inherited
如果你想让一个类和它的子类都包含某一个注解,就可以使用@Inherited 来修饰这个注解。示例:
1 | //运行时注解 |
@Docmented
添加@Docmented 注解,会将该注解在生成 java doc 的时候添加到 doc 文件内容中。
1 | //运行时注解 |
Java 8 注解扩展
类型注解
- 在原有范围上,扩展了注解的使用范围.
1 | eg: |
- 新增 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER(在 Target 上)
1 | ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。 |
- 类型注解的作用
1 | 类型注解被用来支持在Java的程序中做强类型检查。配合第三方插件工具Checker Framework(注:此插件so easy,这里不介绍了),可以在编译的时候检测出runtime error(eg:UnsupportedOperationException; NumberFormatException;NullPointerException异常等都是runtime error),以提高代码质量。这就是类型注解的作用。 |
重复注解 @Repeatable
允许在同一声明类型(类,属性,或方法)上多次使用同一个注解。
Java8 以前的版本使用注解有一个限制是相同的注解在同一位置只能使用一次,不能使用多次。
Java 8 引入了重复注解机制,这样相同的注解可以在同一地方使用多次。重复注解机制本身必须用 @Repeatable 注解。
实际上,重复注解不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。
1 | eg: |