基础篇:带你从头到尾玩转注解

前面写了Android 开发:由模块化到组件化(一),很多小伙伴来问怎么没有Demo啊?之所以没有立刻放demo的原因在还有许多技术点没说完.

今天我们就来细细评味Java当中Annotation,也就是我们常说的注解.

本文按照以下顺序进行:元数据->元注解->运行时注解->编译时注解处理器.


什么是元数据(metadata)

元数据由metadata译来,所谓的元数据就是“关于数据的数据”,更通俗的说就是描述数据的数据,对数据及信息资源的描述性信息.比如说一个文本文件,有创建时间,创建人,文件大小等数据,这都可以理解为是元数据.

在java中,元数据以标签的形式存在java代码中,它的存在并不影响程序代码的编译和执行,通常它被用来生成其它的文件或运行时知道被运行代码的描述信息。java当中的javadoc和注解都属于元数据.

什么是注解(Annotation)?

注解是从java 5.0开始加入,可以用于标注包,类,方法,变量等.比如我们常见的@Override,再或者Android源码中的@hide,@systemApi,@privateApi等

对于@Override,多数人往往都是知其然而不知其所以然,今天我就来聊聊Annotation背后的秘密,开始正文.


元注解

元注解就是定义注解的注解,是java提供给我们用于定义注解的基本注解.在java.lang.annotation包中我们可以看到目前元注解共有以下几个:

  1. @Retention
  2. @Target
  3. @Inherited
  4. @Documented
  5. @interface

下面我们将集合@Override注解来解释着5个基本注解的用法.

@interface

@interface是java中用于声明注解类的关键字.使用该注解表示将自动继承java.lang.annotation.Annotation类,该过程交给编译器完成.

因此我们想要定义一个注解只需要如下做即可,以@Override注解为例

public @interface Override {
}

需要注意:在定义注解时,不能继承其他注解或接口.

@Retention

@Retention:该注解用于定义注解保留策略,即定义的注解类在什么时候存在(源码阶段 or 编译后 or 运行阶段).该注解接受以下几个参数:RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME,其具体使用及含义如下:

注解保留策略 含义
@Retention(RetentionPolicy.SOURCE) 注解仅在源码中保留,class文件中不存在
@Retention(RetentionPolicy.CLASS) 注解在源码和class文件中都存在,但运行时不存在,即运行时无法获得,该策略也是默认的保留策略
@Retention(RetentionPolicy.RUNTIME) 注解在源码,class文件中存在且运行时可以通过反射机制获取到

来看一下@Override注解的保留策略:

@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

这表明@Override注解只在源码阶段存在,javac在编译过程中去去掉该注解.

@Target

该注解用于定义注解的作用目标,即注解可以用在什么地方,比如是用于方法上还是用于字段上,该注解接受以下参数:

作用目标 含义
@Target(ElementType.TYPE) 用于接口(注解本质上也是接口),类,枚举
@Target(ElementType.FIELD) 用于字段,枚举常量
@Target(ElementType.METHOD) 用于方法
@Target(ElementType.PARAMETER) 用于方法参数
@Target(ElementType.CONSTRUCTOR) 用于构造参数
@Target(ElementType.LOCAL_VARIABLE) 用于局部变量
@Target(ElementType.ANNOTATION_TYPE) 用于注解
@Target(ElementType.PACKAGE) 用于包

以@Override为例,不难看出其作用目标为方法:

@Target(ElementType.METHOD)
public @interface Override {
}

到现在,通过@interface,@Retention,@Target已经可以完整的定义一个注解,来看@Override完整定义:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
   
}

@Inherited

默认情况下,我们自定义的注解用在父类上不会被子类所继承.如果想让子类也继承父类的注解,即注解在子类也生效,需要在自定义注解时设置@Inherited.一般情况下该注解用的比较少.

@Documented

该注解用于描述其它类型的annotation应该被javadoc文档化,出现在api doc中.
比如使用该注解的@Target会出出现在api说明中.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
   

    ElementType[] value();
}

这里写图片描述

借助@Interface,@Target,@Retention,@Inherited,@Documented这五个元注解,我们就可以自定义注解了,其中前三个注解是任何一个注解都必备具备的.

你以为下面会直接来将如何自定义注解嘛?不,你错了,我们还是来聊聊java自带的几个注解.

系统注解

java设计者已经为我们自定义了几个常用的注解,我们称之为系统注解,主要是这三个:

系统注解 含义
@Override 用于修饰方法,表示此方法重写了父类方法
@Deprecated 用于修饰方法,表示此方法已经过时
@SuppressWarnnings 该注解用于告诉编译器忽视某类编译警告

如果你已经完全知道这三者的用途,跳过这一小节,直接往下看.

@Override

它用作标注方法,说明被标注的方法重写了父类的方法,其功能类似断言.如果在一个没有重写父类方法的方法上使用该注解,java编译器将会以一个编译错误提示:
这里写图片描述

@Deprecated

当某个类型或者成员使用该注解时意味着
编译器不推荐开发者使用被标记的元素.另外,该注解具有”传递性”,子类中重写该注解标记的方法,尽管子类中的该方法未使用该注解,但编译器仍然报警.

public class SimpleCalculator {
   

    @Deprecated
    public int add(int x, int y) {
        return x+y;
    }
}

public class MultiplCalculator extends SimpleCalculator {
   
    // 重写SimpleCalculator中方法,但不使用@Deprecated
    public int add(int x, int y) {
        return  Math.abs(x)+Math.abs(y);
    }
}

//test code
public class Main {
   

    public static void main(String[] args) {
        new SimpleCalculator().add(3, 4);
        new MultiplCalculator().add(3,5);
    }
}

对于像new SimpleCalculator().add(3,4)这种直接调用的,Idea会直接提示,而像第二种则不是直接提示:
这里写图片描述

但是在编译过程中,编译器都会警告:

这里写图片描述

需要注意@Deprecated和@deprecated这两者的区别,前者被javac识别和处理,而后者则是被javadoc工具识别和处理.因此当我们需要在源码标记某个方法已经过时应该使用@Deprecated,如果需要在文档中说明则使用@deprecated,因此可以这么:

public class SimpleCalculator {
   
    /**
     * @param x
     * @param y
     * @return
     * 
     * @deprecated deprecated As of version 1.1,
     * replace by <code>SimpleCalculator.add(double x,double y)</code>
     */
    @Deprecated
    public int add(int x, int y) {
        return x+y;
    }

    public double add(double x,double y) {
        return x+y;
    }

}
@SuppressWarnning

该注解被用于有选择的关闭编译器对类,方法,成员变量即变量初始化的警告.该注解可接受以下参数:

参数 含义
deprecated 使用已过时类,方法,变量
unchecked 执行了未检查的转告时的警告,如使用集合是为使用泛型来制定集合保存时的类型
fallthrough 使用switch,但是没有break时
path 类路径,源文件路径等有不存在的路径
serial 可序列化的类上缺少serialVersionUID定义时的警告
finally 任何finally字句不能正常完成时的警告
all 以上所有情况的警告

滋溜一下,我们飞过了2016年,不,是看完了上一节.继

  • 30
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值