部分资料来源:http://c.biancheng.net。
一、注解的概念
从 jdk 1.5 之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation)。注解并不能改变程序的运行结果,也不会影响程序运行的性能。有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息。
二、注解的作用
- 生成帮助文档。这是最常见的,也是 Java 最早提供的注解。常用的有
@see
、@param
和@return
等; - 跟踪代码依赖性,实现替代配置文件功能。比较常见的是
Spring 2.5
开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量; - 在编译时进行格式检查。如把
@Override
注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。
三、常用的注解
简介
在 jdk 1.8 中提供了 11 种内部注解,其中有 5 个基本注解、6 个元注解。
基本注解包括:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs 和 @FunctionalInterface。
使用
@Override 注解
作用目标:成员方法
作用:对方法进行对父类方法重写的检查,保证重写的一定是父类原有的方法。(重写就是子类重新定义覆盖父类原有的方法,方法签名保持一致,但其内容被改变。)
例:如下代码中是一个 Person
类中对 toString
方法的覆盖重写(toString
方法是 Object
类中的方法),如果方法签名与父类不一致,则编译会报错。
1 | /* Person.java */ |
@Deprecated 注解
作用目标:构造方法、成员变量、局部变量、方法、参数、类等
作用:用于将某个元素标记为过时的元素,使用过时的元素在编译时会给出警告。(在现代 IDE 中,如 IntelliJ IDEA
,使用过时的元素会有删除线的效果)
@SuppressWarnings 注解
作用目标:构造方法、成员变量、局部变量、方法、参数、类等
作用:消除警告
关键字 | 用途 |
---|---|
all | 抑制所有警告 |
boxing | 抑制装箱、拆箱操作时候的警告 |
cast | 抑制映射相关的警告 |
dep-ann | 抑制启用注释的警告 |
deprecation | 抑制过期方法警告 |
fallthrough | 抑制在 switch 中缺失 breaks 的警告 |
finally | 抑制 finally 模块没有返回的警告 |
hiding | 抑制相对于隐藏变量的局部变量的警告 |
incomplete-switch | 忽略不完整的 switch 语句 |
nls | 忽略非 nls 格式的字符 |
null | 忽略对 null 的操作 |
rawtypes | 使用 generics 时忽略没有指定相应的类型 |
restriction | 抑制禁止使用劝阻或禁止引用的警告 |
serial | 忽略在 serializable 类中没有声明 serialVersionUID 变量 |
static-access | 抑制不正确的静态访问方式警告 |
synthetic-access | 抑制子类没有按最优方法访问内部类的警告 |
unchecked | 抑制没有进行类型检查操作的警告 |
unqualified-field-access | 抑制没有权限访问的域的警告 |
unused | 抑制没被使用过的代码的警告 |
例:消除使用过期方法的警告
1 | /* Student.java */ |
@SafeVarargs 注解
作用目标:构造方法、方法(不适用于非 static 或 非 final 方法,请改用 @SuppressWarnings
注解)
作用:消除来自形参化 vararg 类型的可能的堆污染警告
堆污染是一个技术术语。它指的是引用的类型不是它们指向的对象的父类型。所以可能会引发异常
例:消除可能存在堆污染的警告
1 |
|
@FunctionalInterface 注解
作用目标:接口
作用:检查并保证该接口为函数式接口(接口中仅包含一个抽象方法,如果没有或有多个抽象方法,编译器会报错)
1 | /* IFunctionalTest.java */ |
四、元注解与自定义注解
简介
元注解
在 jdk 1.8 中提供了6 个元注解(元注解就是注解的注解,定义一个新注解需要用到元注解)
包括:@Documented、@Target、@Retention、@Inherited、@Repeatable 和 @Native。其中 @Repeatable 和 @Native 注解是 jdk 1.8 新增。
自定义注解
格式:
1 | /* 定义 */ |
注意:
- 访问修饰符只能是 public 和 (default)(表示不写、默认,并非是关键字 default),无论是注解还是注解的成员变量。
- 成员变量名后要加一对小括号。
- 注解的成员变量可以有默认值,通过
default
关键字指定默认值。 - 如果注解只有一个成员变量并且名字为
value
,那么则可以直接写成 @注解名(值) 的形式。
使用
@Documented
在默认情况下,Java doc
不会提取注解信息,但是如果声明注解时使用了 @Documented
注解,那么该注解就能够被 Java doc
提取到。
@Target
@Target
注解用来指定注解的作用目标,也就是该注解能够用在哪些目标下。目标有以下几种:
名称 | 说明 |
---|---|
CONSTRUCTOR | 用于构造方法 |
FIELD | 用于成员变量(包括枚举常量) |
LOCAL_VARIABLE | 用于局部变量 |
METHOD | 用于方法 |
PACKAGE | 用于包 |
PARAMETER | 用于类型参数(JDK 1.8新增) |
TYPE | 用于类、接口(包括注解类型)或 enum 声明 |
上述值是枚举类型 ElementType
的值,存在于 java.lang.annotation
包下。
1 | /* MyAnnotation.java */ |
以上代码是 @Target
注解的使用示例,@MyAnnotation
注解适用于成员变量,如果放在其他元素上会导致编译不通过。
@Retention
@Retention
注解用于指定注解的生命周期,生命周期有以下 3 种:
名称 | 说明 |
---|---|
SOURCE | 在资源文件中有效,注解将被编译器丢弃 |
CLASS | 会被编译器记录在 .class 文件中,但在运行时不会被 VM 保留 |
RUNTIME | 在运行时有效,运行时会被 VM 保留并可能被反射获取到 |
上述值是枚举类型 RetentionPolicy
的值,同样存在于 java.lang.annotation
包下。
@Repeatable
@Repeatable
可以使同一个注解在同一个目标上重复使用,但是需要一个容器注解配合。
在没有 @Repeatable
注解时在同一个目标上重复使用同一个注解:要套壳。
1 | /* MyAnnotation.java */ |
使用 @Repeatable
注解:可在同一目标直接上重复使用注解。
1 | /* MyAnnotation.java */ |
注意:无论有没有 @Repeatable
注解都需要一个容器注解用于存放注解,使用了 @Repeatable
注解只是在重复使用同一注解时省略了套壳而已。
@Inherited
正常情况下,子类继承父类时,不会将作用在父类上的注解也继承过来。
而 @Inherited
注解的作用就是让子类继承父类的时候将注解也一并继承过来。
请看以下代码:
1 | /* MyAnnotation.java */ |
以上代码中定义了一个 @MyAnnotation
注解,Person
类和 Student
类,注解作用在Person
类上 Student
类继承自 Person
类。我们在测试代码中浅浅的使用了 Java 的反射特性来获取作用在类上的注解,但是没有任何结果,说明作用在其上的注解并没有被继承过来。
我们为 @MyAnnotation
注解加上 @Inherited
试试:
1 | /* MyAnnotation.java */ |
可以看到有结果了,打印出来了完整注解名和其中成员变量的值,说明该注解有被子类继承过来。
反射在下一篇章,也有些书籍或教程上会先讲反射再讲注解。
@Native
@Native
注解只适用于字段。它指示带注释的字段是可以从本机代码引用的常量。例:它在 Integer
类中是如何使用的:
1 | public final class Integer { |
该注解还可以作为工具生成一些辅助头文件的提示。
@Native 注解这段解释搬运自
https://www.baeldung.com/java-default-annotations
由于本人能力有限,实在不知道该注解的作用以及如何使用,所以就直接把该网站上对其的解释搬运过来了