注解其实在其他语言也有, 只是叫法不一样, 在 C# 中叫特性, 其实都是一个意思. 今天就是了解下 Java 的注解.
一, 什么是注解
我们先看官方解释: 它提供了一种安全的类似注释的机制, 用来将任何的信息或元数据 (metadata) 与程序元素 (类, 方法, 成员变量等) 进行关联. 为程序的元素 (类, 方法, 成员变量) 加上更直观更明了的说明, 这些说明信息是与程序的业务逻辑无关, 并且供指定的工具或框架使用. Annontation 像一种修饰符一样, 应用于包, 类型, 构造方法, 方法, 成员变量, 参数及本地变量的声明语句中. Java 注解是附加在代码中的一些元信息, 用于一些工具在编译, 运行时进行解析和使用, 起到说明, 配置的功能. 注解不会也不能影响代码的实际逻辑, 仅仅起到辅助性的作用. 包含在 java.lang.annotation 包中.
看着上面的解释是不是还是一头雾水, 其实我们可以更通俗的理解. 最近几年出现一个词斜杠青年还有黄晓明拍摄的广告语: 给人贴标签, 下定义, 总是很容易, 而我却不会因为一件事被定性, 这里的斜杠青年, 贴标签都是把某些属性附加给对象, 个人觉得和注解功能差不多, 它提供了一种安全的类似注释的机制, 用来将任何的信息或元数据 (metadata) 与程序元素 (类, 方法, 成员变量等) 进行关联. 我们可以再来理解下这句话, 这里的程序元素可以理解为人, 信息或元数据理解为标签, 把标签属性 (信息或元数据) 给赋给人(程序元素).
二, 元注解
我们在新建注解时, 会有下图的一些选项, 这里我们可以看到 @Retention,@Target,@Documented 这几个词, 还有这几个对应的复选框, 那这些是什么呢? 这些其实就是元注解.
java.lang.annotation 提供了五种元注解, 专门注解其他的注解(在自定义注解的时候, 需要使用到元注解):
@Documented - 注解是否将包含在 JavaDoc 中
@Retention - 什么时候使用该注解
@Target - 注解用于什么地方
@Inherited - 是否允许子类继承该注解
@Repeatable - 指定注解可重复使用
1.@Retention- 定义该注解的生命周期
RetentionPolicy.SOURCE : 在编译阶段丢弃. 这些注解在编译结束之后就不再有任何意义, 所以它们不会写入字节码.@Override, @SuppressWarnings 都 属于这类注解.
RetentionPolicy.CLASS : 在类加载的时候丢弃. 在字节码文件的处理中有用. 注解默认使用这种方式
RetentionPolicy.RUNTIME : 始终不会丢弃, 运行期也保留该注解, 因此可以使用反射机制读取该注解的信息. 我们自定义的注解通常使用这种方式.
2.Target - 表示该注解用于什么地方. 默认值为任何元素, 表示该注解用于什么地方. 可用的 ElementType 参数包括
ElementType.CONSTRUCTOR: 用于描述构造器
ElementType.FIELD: 成员变量, 对象, 属性(包括 enum 实例)
ElementType.LOCAL_VARIABLE: 用于描述局部变量
ElementType.METHOD: 用于描述方法
ElementType.PACKAGE: 用于描述包
ElementType.PARAMETER: 用于描述参数
ElementType.TYPE: 用于描述类, 接口(包括注解类型) 或 enum 声明
3.@Documented - 一个简单的 Annotations 标记注解, 表示是否将注解信息添加在 java 文档中.
4.@Inherited - 定义该注释和子类的关系
@Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的. 如果一个使用了 @Inherited 修饰的 annotation 类型被用于一个 class, 则这个 annotation 将被用于该 class 的子类.
5.@Repeatable - 指定该注解可重复使用
使用 @Repeatable 修饰表示该注解可以为重复使用.
三, 内置注解
@Deprecated
编译器在编译阶段遇到这个注解时会发出提醒警告, 告诉开发者正在调用一个过时的元素比如过时的方法, 过时的类, 过时的成员变量.
@Override
提示子类要复写父类中被 @Override 修饰的方法
@SuppressWarnings
阻止警告的意思. 调用被 @Deprecated 注解的方法后, 编译器会警告提醒, 而有时候开发者会忽略这种警告, 他们可以在调用的地方通过 @SuppressWarnings 达到目的.
@SafeVarargs
参数安全类型注解. 它的目的是提醒开发者不要用参数做一些不安全的操作, 它的存在会阻止编译器产生 unchecked 这样的警告. 它是在 Java 1.7 的版本中加入的.
@FunctionalInterface
函数式接口注解, 这个是 Java 1.8 版本引入的新特性. 函数式编程很火, 所以 Java 8 也及时添加了这个特性. 函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口.
四, 自定义注解
这里参考了写 C# 特性使用的例子. 用来演示自定义注解以及注解的继承.
1. 这个定义了 CustormDescription 注解, 相当于标签, 为了能多贴标签又定义了注解容器 CustormDescriptions.
CustormDescription 注解:
- package CusAnnontation;
- import static java.lang.annotation.RetentionPolicy.RUNTIME;
- import static java.lang.annotation.ElementType.TYPE;
- import java.lang.annotation.Documented;
- import java.lang.annotation.Inherited;
- import java.lang.annotation.Repeatable;
- import java.lang.annotation.Retention;
- import java.lang.annotation.Target;
- @Documented
- @Retention(RUNTIME)
- @Target(TYPE)
- @Inherited
- @Repeatable(CustormDescriptions.class)
- public @interface CustormDescription {
- String description() default "";
- }
CustormDescriptions 容器:
- package CusAnnontation;
- import static java.lang.annotation.ElementType.TYPE;
- import static java.lang.annotation.RetentionPolicy.RUNTIME;
- import java.lang.annotation.Documented;
- import java.lang.annotation.Inherited;
- import java.lang.annotation.Retention;
- import java.lang.annotation.Target;
- @Documented
- @Retention(RUNTIME)
- @Target(TYPE)
- @Inherited
- public @interface CustormDescriptions {
- CustormDescription[] value();
- }
2. 实现继承关系
这里还是创建了 Person 和 Student 类, Studnet 继承 Person.
Person: 这里为 Person 定义了多个注解, 相当于为它贴了多个标签.
- package CusAnnontation;
- @CustormDescription(description="基类")
- @CustormDescription(description="人")
- public class Person {
- private String Name;
- private int Age;
- public String getName() {
- return Name;
- }
- public void setName(String name) {
- Name = name;
- }
- public int getAge() {
- return Age;
- }
- public void setAge(int age) {
- Age = age;
- }
- }
Student: 这里 Student 也贴了一个标签.
- package CusAnnontation;
- //@CustormDescription(description="基类")
- //@CustormDescription(description="人")
- @CustormDescription(description="学生")
- public class Student extends Person {
- private String StudentId;
- public String getStudentId() {
- return StudentId;
- }
- public void setStudentId(String studentId) {
- StudentId = studentId;
- }
- }
3. 获取注解属性值
这里来获取出 Student 类的 CustormDescriptions 的注解值.
- package CusAnnontation;
- public class AnnontationTest {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- CustormDescriptions custormDescriptions =new Student().getClass().getAnnotation(CustormDescriptions.class);
- for(CustormDescription h: custormDescriptions.value()){
- System.out.println("description:" + h.description());
- }
- }
- }
那问题来了, 按照上面增加的注解会打印出什么呢? 因为是获取学生的注解, 会不会打印出 description: 学生呢? 我们可以打印一下, 看下结果.
description: 基类
description: 人
其实, 这里只打印出了上面父类的注解信息, 并未打印出子类的, 如果要打印出子类的, 需要在子类上加上一个父类的注解, 这样就会覆盖掉父类的注解. 例如修改下 Student 类如下.
- package CusAnnontation;
- @CustormDescription(description="基类")
- //@CustormDescription(description="人")
- @CustormDescription(description="学生")
- public class Student extends Person {
- private String StudentId;
- public String getStudentId() {
- return StudentId;
- }
- public void setStudentId(String studentId) {
- StudentId = studentId;
- }
- }
修改之后再运行, 就会输出学生注解了, 结果为:
description: 基类
description: 学生
4.@Retention 生命周期
在上面的 demo 中我们也可以验证下 @Retention 的生命周期, 在自定义注解一般使用 @Retention(RUNTIME), 如果我们把上面的改成 CLASS, 那就会直接报错, 因为 custormDescriptions 对象是一个 null 空值.
来源: https://www.cnblogs.com/5ishare/p/9363581.html