Java系列之注解

Java 注解(Annotation)又称之为 Java 标注、元数据,是 Java 1.5 之后加入的一种特殊语法,通过注解可以标注 Java 中的类、方法、属性、参数、包等,可以通过反射原理对这些元数据进行访问,注解的使用不会影响程序的正常运行,只会对编译器警告等辅助工具产生影响。

注解功能

  • 编译器可以使用注解来检测错误和取消警告;
  • 使用注解可以生成特定代码,如 ButtferKnife 使用注解简化 findViewById等;
  • 某些注解可以在运行时进行检查和操作。

定义注解

注解的定义使用 @interface 作为关键字,实际上表示自动继承了 java.lang.annotation.Annotation 接口,定义格式参考如下:

@元注解
public @interface AnnotationName{
    //配置参数(参数类型 参数名称())
    String name() default "hello";
}

配置参数里面的类型包括基本类型、String、class、枚举以及相关类型的数组,可以使用 default 设置配置参数的默认值,定义一个注解具体如下:

@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDefineAnnotation {
    String[] name() default "test";
}

内置注解

  1. @Override
  2. @Deprecated
  3. @SuppressWarnings

下面是上面三个内置注解的声明:

//表示当前的方法将覆盖超类中的方法,编译时进行格式检查
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
//表示一个类或者是方法不再建议使用,将其标记为过时,但还是可以使用
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
//表示关闭不当的编译器警告信息
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

根据对上面三个注解的声明来看,@SuppressWarnings 中定义了一个数组,这个数组表示在该注解上具体的目标是那些,如可在 SuppressWarnings 上使用的值,常用的具体如下:

  • deprecation:使用了过时的类或方法时的警告
  • unused:有未使用的变量时的警告
  • unchecked:执行了未检查的转换时的警告
  • fallthrough:当 switch 程序块直接通往下一种情况而没有 break 时的警告
  • path:在类路径、源文件路径等中有不存在的路径时的警告
  • serial:当在可序列化的类上缺少serialVersionUID 定义时的警告
  • finally :任何 finally 子句不能正常完成时的警告
  • all:关于以上所有情况的警告

下面看一个案例,具体如下:

public void test() {
    long date = Date.parse("2018-04-22");
}

上面的代码如果使用 eclipse 等其他 IDE 时会出现两个警告,一是使用了过时的 API,二是变量 date 赋值后没有被使用过,警告截图如下:

01.png

当然, IDE 会提示是否添加 SuppressWarnings 来取消这些警告,前文中可以看到注解 @SuppressWarnings 的声明中需要配置参数,这个参数是一个数组,数组名称是 value,可以省略这个名称, 具体如下:

//不省略
public void test2() {
    @SuppressWarnings(value= {"deprecation", "unused"})
    long date = Date.parse("2018-04-22");
}
//省略
public void test2() {
    @SuppressWarnings({"deprecation", "unused"})
    long date = Date.parse("2018-04-22");
}

来张截图说明一下使用 @SuppressWarnings 的效果,具体如戏:

02.png

如果只想取消一种警告可以这样写,具体如下:

//第一种
public void test2() {
    @SuppressWarnings(value = {"deprecation"})
    long date = Date.parse("2018-04-22");
    System.out.println(date);
}
//第二种
public void test2() {
    @SuppressWarnings({"deprecation"})
    long date = Date.parse("2018-04-22");
    System.out.println(date);
}

注意:如果在定义注解的配置参数名称为 value,那么可以在配置注解时可以省略 value,反之,使用其他名称,则必须采用第一种方式,要指定配置参数名称。

当然其他注解和 @SuppressWarnings 也比较类似, @Override、@Deprecated 由它们的声明可知直接使用即可,不需要指定具体目标,在其声明注解时用到了 @Documented、@Retention、@Target 等,这些用来注解其他注解的特殊注解称之为元注解,具体请看下文。

元注解

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

@Target 用来描述注解的使用范围,它的声明如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

由 @Target 声明可知使用 @Target 注解必须指定具体的 Java 成员,也就是该注解要使用到哪个位置,具体由枚举 ElementType 中定义,具体如下:

public enum ElementType {
    TYPE,           //类、接口、注解、枚举
    FIELD,          //属性(包括枚举常量) 
    METHOD,         //方法
    PARAMETER,      //参数 
    CONSTRUCTOR,    //构造方法
    LOCAL_VARIABLE, //局部变量
    ANNOTATION_TYPE,//注解
    PACKAGE,        //包

    /**
     * 类型注解
     * @since 1.8
     */
    TYPE_PARAMETER,
    TYPE_USE
}
@Retention

@Retention 表示在什么级别保存该注解的信息,它的声明如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

由 @Retention 的声明可知,使用 @Retention 时,必须指定保存celue(RetentionPolicy),具体值如下:

public enum RetentionPolicy {
    SOURCE,  //在编译时会被丢弃,仅仅在源码中存在
    CLASS,   //默认策略,运行时就会被丢弃,仅仅在 class 文件中
    RUNTIME  //编译时会将注解信息记录到class文件,运行时任然保留,可以通过反射获取注解信息
}

@Documented@Inherited 都没有配置参数,是一种标记注解,@Documented 表示将该注解显示到用户文档中,@Inherited 表示该注解只有使用在类上才会有效,而且该注解会被子类继承。

类型注解

在对元注解的说明中可知从 Java8 开始新增了类型注解,如果在注解 @Target 使用这种注解,表明该注解可以在对应的任何地方使用,如在 @Target 中指定 TYPE_PARAMETER 就可在自定义类型的声明处使用该注解,如在 @Target 中指定 TYPE_USE 就可在任何类型前添加该类之间,主要是方便 Java 开发者使用类型注解和相关插件(Checker Framework)来检查来在编译期检查运行时的异常。

下面分别定义指定 TYPE_PARAMETER 和 TYPE_USE 的注解,具体如下:

//1. TYPE_PARAMETER
@Target(value = {ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
    String value();
}
//2. TYPE_USE
@Target(value = ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeUseAnnotation {
}

然后,在下面的案例中使用这两个注解,具体如下:

/**
 * 测试注解
 * @author jzman
 */
public class TestAnnotation {

    //...
    
    /**
     * ElementType.TYPE_PARAMETER
     * 使用在自定义类型声明的时候,如注解@TypeParameterAnnotation
     * @param <T>
     */
    static class TypeAnnotationA<@TypeParameterAnnotation(value="hello") T>{
        /**
         * ElementType.TYPE_USE
         * 可以使用在任意类型前面(包含TYPE_PARAMETER)
         */
        //创建实例
        MyType myType = new @TypeUseAnnotation MyType();
        //对象类型
        Object obj = (@TypeUseAnnotation Object) myType;
        //泛型
        ArrayList<@TypeUseAnnotation T> list = new ArrayList<>();
        //参数中的类型
        public String testA(@TypeUseAnnotation String test) {
            return "Hello"+test;
        }
        //枚举
        public void testB(@TypeUseAnnotation Color color) {
            //...
        }
        
        enum Color{
            RED, GREEN, BLUE
        }
    }
    
    static class MyType{}   
}

其实注解的语法比较简单,仅仅定义注解对实际开发是没有帮助的,觉得注解只有在运行时通过反射获取注解信息才是最重要的,注解与反射相关的内容会在以后的推文中学习,到此对注解的认识就结束了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容