Java中的注解

1. 注解

注解是JDK5.0才开始引入的技术.

1.1 什么是注解

以下引用维基百科的解释,看不懂也没事,我会做进一步解释。
Java的注解是一种声明元数据的语法, 所谓的元数据.我在前面的文章有做过解释,即是用来描述数据的数据.

  • 注解是一种元数据, 它可以被加到Java源代码方法变量参数.
  • 注解描述的信息可以从源码中读出,并且因为注解可以嵌入到字节码文件,所以注解也可以被保留到运行时, 并通过反射的技术进行读取注解信息.

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code.[1] Classes, methods, variables, parameters and packages may be annotated. Like Javadoc tags, Java annotations can be read from source files. Unlike Javadoc tags, Java annotations can also be embedded in and read from class files generated by the compiler. This allows annotations to be retained by Java VM at run-timeand read via reflection.[2] It is possible to create meta-annotations out of the existing ones in Java.[3]

1.2 注解的语法

通过上面的介绍,我们可以推断注解主要是用在为java中的各种类型提供元数据, 并结合反射技术, 在运行时进行读取。我们先来了解下注解的语法, 写个最简单的注解.

语法

注解的语法和接口的语法十分类似。它们最显著的不同在于, 注解类型的关键字是@interface, 其次注解是使用方法类声明元素.注解中声明的一个方法表示这个注解类拥有的这个元素和其对应的类型。如下


[public | 缺省] @interface 注解名() {
         返回类型  元素名或者说是属性名() default 默认值;
}
  • 修饰符
    可以是public或者缺省
  • 返回类型(属性类型)
    返回类型其实就是注解类中元素的类型或者说是其属性的类型。注解的返回类型只能是基本数据类型枚举注解或者是这四种类型构成的数组

最简单的注解

package com.sweetcs.study.annotation;

public @interface MyAnnotation {
    
}

测试下注解可以加入的位置,如下代码.直接给包贴上注解是会报错的.其他测试都没有问题, 在实际中在包上贴注解也不是很常用,后面在进行研究.

//@MyAnnotation
package com.sweetcs.study.annotation;

@MyAnnotation
public class TestAnnaotation {
    
    @MyAnnotation
    public void myPrint(@MyAnnotation int a, @MyAnnotation Object b) {
        
    }
    
    public static void main(String[] args) {
        @MyAnnotation
        String name = "test";
        System.out.println(name);
        
    }
}

尝试了注解的最简单的结构。同时我们也尝试了在类上贴上注解、在方法上贴上注解、在参数上贴上注解、在变量上贴上注解.那么它们有什么作用呢? 我们以Java中内建的常用三个注解来举例。

1.3 Java内建的三个注解

  • @SuppressWarnings 抑制警告信息注解
    该注解的功能是消除编译器的警告信息。如下我是贴在year变量上,所以只对year变量有用.并且主要是抑制变量没有使用警告方法过期警告
    @Test
    public void testBuildInAnnotation() {
        
        Date date = new Date();
        @SuppressWarnings({ "deprecation", "unused" })
        int year = date.getYear();
        
    }
  • @Deprecated 该注解表示该方法已经过期,不推荐使用,如果使用编译器会给出警告
    @Deprecated
    public int getYear() {
        return normalize().getYear() - 1900;
    }

上面代码即使Date类中的getYear

  • @Override 检查贴上取的方法是否是复写父类的方法.
    该注解一般是用在子类的同名方法中,用来保证子类的同名方法是在复写父类的方法。如果不是, 贴上该注解的方法编译器会给出错误,让你去更改

以上三个注解在java的经常的使用, 我们打开@Override和@注解,看起内部的定义的。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
  • 可以很清楚的看到这两个注解,没有任何的成员方法.所以这两个注解其实只是相当于一个标签, 贴上标签后结合编译器和运行时可以玩出不同花样。
  • 还可以看出注解的上面还有@Target@Retention@Documented三个注解.这三个注解又称为元注解, 即描述注解的注解.下面我们分别介绍下这三个注解的功能.

元注解

  • @Target
    该元注解可以贴在那个地方,即使由这个注解来指定说明.其描述的属性是一个value数组,数组元素的类型是ElmentType枚举类型
public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}
  • @Retention
    该元注解表示保留注解到哪个阶段,即用于指定注解的生命周期。其描述注解的生命周期的属性类型是枚举.
public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}
  • @Documented
    该元注解用来表示贴在这些地方的注解是否可以被javadoc解析.贴上了@Documented表示可以被解析。
  • @ Inherited
    该元注解用来表示一个类的注解是否可以被子类继承.默认是不行的.

我们开发中最常用的是前两个,也是必不可少的。

1.4 自定义注解

接扫了注解的定义语法, 描述注解的元注解.我们可以自己进行自定义注解了.

  • 对于返回值是数组类型的,我们可以使用 方法名 = {类型值1, 类型值2}进行赋值
  • 并且我们需要用元注解, 说明自定义注解的生命周期和注解可以贴在哪些地方

自定义步骤

  1. 声明注解类型和其方法.
  2. 使用元注解@Retation和@Target说明注解的使用范围和注解的生命周期
package com.sweetcs.study.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER})
public @interface MyCustomerAnnotation {

    String name() default "SweetCS";
    
    String[] sons();
}
    @Test
    public void testCustomerAnnotation() {
        
        @MyCustomerAnnotation(name="test", sons={"son1", "son2"})
        int a = 1;
        
    }

1.5 注解的本质和便捷写法

注解的本质

我们写的注解底层其实是会在编译阶段自动的编译成继承java.lang.annotation.Annotation接口的接口.但是不允许我们显示的继承自该接口.

注解简便写法

阅读底层注解的实现代码,我们会发现其方法名无论返回类型是啥。方法名都清一色的命名为value.这是因为Java的注解中规定.只要方法名为value, 在使用注解的时候, 不同指定注解的元素名.如下代码

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

我们在使用@Retention的使用不需要指定元素名.默认就是value

1.6 获取注解信息

  • 为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素.实现了AnnotatedElement的类.可以看到实现AnnotatedElement接口的类都是和反射相关的类, 所以我们必须结合反射技术来获取注解信息.
image.png

下面代码, 使用反射来获取Father类的Class对象, 然后获取其上的注解

    @Test
    public void testCustomerAnnotation() {
        
        // 获取Father类上的注解
        MyCustomerAnnotation annOfFatherClass = Father.class.getAnnotation(MyCustomerAnnotation.class);
        System.out.println(annOfFatherClass);
        System.out.println(annOfFatherClass.name() + "  " + Arrays.toString(annOfFatherClass.sons()));
    
    }

运行输出

后续

注解使用场景主要是反射结合使用. 因为反射结合使用, 所以注解使用的最多的是在类上和类中的成员上.后续我会分享基于注解和反射来动态的创建表.可以大大提高我们的编程体验, 让我们无需去关注建表语句.

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