Java 注解 @Annotation

同时发布于 知乎 Java 注解

1.注解的由来

在引入注解之前,在不同类型的应用程序使用XML作为标准的代码配置机制,程序员们描述其代码的形式尚未标准化,每个人的做法各异:transient关键字、注释、接口等,代码和XML的解耦以及未来对这种解耦应用的维护并不低廉,这显然不是一种优雅的方式,随之而来的JDK5.0引入一种崭新的记录元数据的形式——注解被引入到Java中。
它的作用是修饰编程元素。什么是编程元素呢?例如:包、类、构造方法、方法、成员变量等。

2.什么是注解

DK5.0中的类型:1、类(class)2、接口(interface)3、枚举(enum)4、注解(Annotation)
因此,注解与其他3种类型一样,都可以定义、使用,以及包含有自己的属性、方法

注解分类

(1)标记注释:注解的内部没有属性,称作标记注解
使用方法:@注解名
使用例子:@MarkAnnotation

(2)单值注解:注解的内部只有一个属性,称作单值注解
使用方法:@注解名(属性名=属性值)
使用例子:@SingleAnnotation(value="abc") //也可以写成@SingleAnnotation("abc")
*(属性名=属性值)可以简化为(属性值),但是需要满足以下两个条件:
1、该注解必须为单值注解
2、该注解的属性名必须为value

(3)多值注解:注解的内部有多个属性,称作多值注解
使用方法:@注解名(属性名1=属性值1, 属性名2=属性值2……)
使用例子:@MultipliedAnnotation(value1 = "abc", value2 = 30……)

元注解

Java提供了一下几个元注解(下边会有介绍)

Target、Retention、Documented和Inherited

元注解的作用:

可以用于注解类(annotate Classes)

可以用于注解接口(annotate Interfaces)

可以用于注解枚举类型(annotate Enums)

因此注解同样也可以用于注解注解(annotate Annotations)

3.注解语法

3.1注解声明介绍

我们通过一个简单的例子了解下:

<pre>
@Annotation
@Annotation1(info = "I am Annotation")
public AnnotationMethod(){
//代码块
}
</pre>

从代码一步步看:

01.使用“@”作为前缀声明一盒注解,向编译器说明,该元素(Annotation)是注解

02.注解后面(),标注它的属性,采用键值对的形式,如果注解只有一个元素(或者只需要指定一个元素的值,其它则使用默认值),也可以表示成: @Annotation("I am Annotation");如果没有元素需要被指定,则不需要括号;

03.注解用途:可以标注在Java程序的每一个元素上使用:类,域,方法,包,变量等

3.2 预定义的注解

3.2.1 @Override

目的在于标识某一个方法是否覆盖了它的父类的方法

3.2.2 @Deprecated

属于标记注解.所谓标记注解,就是在源程序中加入这个标记后,并不影响程序的编译,但有时编译器会显示一些警告信息。

用于标明已经过时的方法或类

3.2.3 @SuppressWarnnings

用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告.

<pre> <code>

deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告;
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
finally:任何 finally 子句不能正常完成时的警告;
all:关于以上所有情况的警告。

</code></pre>

4.定义注解

4.1 注解的定义

<pre> <code>
public @interface CustomAnnotationClass
</code></pre>

其中** @interface**说明这是一个自定义注解的定义.

4.2 定制化

定制化时,有很多其它属性可以用在自定义注解上,但是 ==目标(Target==)和 ==保留策略(Retention Policy==)是最重要的两个。

4.2.1 @Target

用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型

<pre>
<code>
public enum ElementType {
/*标明该注解可以用于类、接口(包括注解类型)或enum声明/
TYPE,

/** 标明该注解可以用于字段(域)声明,包括enum实例 */
FIELD,

/** 标明该注解可以用于方法声明 */
METHOD,

/** 标明该注解可以用于参数声明 */
PARAMETER,

/** 标明注解可以用于构造函数声明 */
CONSTRUCTOR,

/** 标明注解可以用于局部变量声明 */
LOCAL_VARIABLE,

/** 标明注解可以用于注解声明(应用于另一个注解上)*/
ANNOTATION_TYPE,

/** 标明注解可以用于包声明 */
PACKAGE,

/**
 * 标明注解可以用于类型参数声明(1.8新加入)
 * @since 1.8
 */
TYPE_PARAMETER,

/**
 * 类型使用声明(1.8新加入)
 * @since 1.8
 */
TYPE_USE

}
</code>
</pre>

  • 当注解未指定Target值时,则此注解可以用于任何元素之上
  • 设定一个值:@Target(ElementType.METHOD)
  • 设定多个值时使用{}包含并用逗号隔开

<code>@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})</code>

4.2.2 @Retention

用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime)

  • SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

  • CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等

  • RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。

4.2.3 注解中的细节

<pre>
<code>
@Target(ElementType.TYPE) // 注解在类上
@Retention(RetentionPolicy.RUNTIME) //保留在运行时
@interface CustmAnnotationClass {
/**
* Java 元注解
* 1. 目标
* 2. 保留策略
*/
String info() default "Annotation";

}

@CustmAnnotationClass(info="OOP")
public class Test {

}
</code>
</pre>

  1. 注解支持以下类型:
  • 所有基本类型
    (int,float,boolean,byte,double,char,long,short)
  • String
  • Class
  • enum
  • Annomation
  • 上述类型的数组形式
  1. 注解可以作为元素的类型,也就是嵌套注解
  2. 注解默认值限制

元素必须要么具有默认值,要么在使用注解时提供元素的值。

对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值.当然我们可以定义一些特殊的值,例如空字符串或负数,表示某个元素不存在

4.2.4 注解中的继承
  • 注解不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口
  • @Inherited

使用了保留注解@Inherited,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解,换句话说:这个类将自动地把这个注解传递到所有子类中而不用在子类中声明,通常,一个类继承了父类,并不继承父类的注解。这完全和使用注解的目的一致的:提供关于被注解的代码的信息而不修改它们的行为。

所以:@Inheriated注解仅在存在继承关系的类上产生效果,在接口和实现类上并不工作。
在默认的情况下,父类的注解并不会被子类继承。如果要继承,就必须加上Inherited注解。

可以点击这里看看哦

4.2.5 其他注解属性介绍
  • @Documented 被修饰的注解会生成到javadoc中

5.注解的获取(反射)

  • 在使用反射之前必须使用import java.lang.reflect.* 来导入和反射相关的类。

  • 如果要得到某一个类或接口的注解信息,可以使用如下代码:

<pre>
<code>
Annotation annotation = TestAnnotation.class.getAnnotation(MyAnnotation.class);
</code>
</pre>

  • 如果要得到全部的注解信息可使用如下语句:

<pre>
<code>
Annotation[] annotations = TestAnnotation.class.getAnnotations();

Annotation[] annotations = TestAnnotation.class.getDeclaredAnnotations();

</code>
</pre>

getDeclaredAnnotations与getAnnotations类似,但它们不同的是getDeclaredAnnotations得到的是当前成员所有的注解,不包括继承的。而getAnnotations得到的是包括继承的所有注解。

  • 如果要得到其它成员的注解,可先得到这个成员,然后再得到相应的注解。如得到myMethod的注解。

<pre>
<code>
Method method = TestAnnotation.class.getMethod("myMethod", null);

Annotation annotation = method.getAnnotation(MyAnnotation.class);
</code>
</pre>
注:要想使用反射得到注解信息,这个注解必须使用

@Retention(RetentionPolicy.RUNTIME)进行注解。

  • 判断指定类型的注解是否存在于此元素

<pre>
<code>
isAnnotationPresent(Class<? extends Annotation> annotationClass)
</code>
</pre>

6. Java8 变化

  • @Repeatable
    表示在同一个位置重复相同的注解
    <code>@Repeatable(MyAnnotation.class)</code>
  • 新增ElementType

TYPE_PARAMETER 和 TYPE_USE ,在Java8前注解只能标注在一个声明(如字段、类、方法)上,Java8后,新增的TYPE_PARAMETER可以用于标注类型参数,而TYPE_USE则可以用于标注任意类型(不包括class)。

7.自动测试机的写法

7.1自动测试机的原理:

使用Annotation来Annotate元素的实质是:每一个ElementType内部的元素都有两个方法,分别为
(注:为方便理解,以下使用的TestCase为某个特定的自定义注释)

(1)isAnnotationPresent(TestCase.class) //判断该元素是否被TestCase所注释

(2)getAnnotation(TestCase.class) //获得TestCase的类对象

7.2自动测试机的工作过程是:

(1)首先通过反射,获得被测类o中的每一个方法

(2)对每一个方法通过使用isAnnotationPresent(TestCase.class)判断其是否被TestCase所注释(注意是.class!)

(3)如果某方法method被TestCase所注释,则通过method的getAnnotation(TestCase.class)获得TestCase的类对象tc

(4)通过tc的value()方法,获得该类对象的属性value

(注:此处使用的value()方法,正是在TestCase中定义的value属性,再次理解在注释中定义的value既是属性,也是方法)

(5)调用method方法的invoke(o,value),用value对method进行测试

参考福利

官方Java注解地址:http://docs.oracle.com/javase/tutorial/java/annotations/

维基百科中关于Java注解的解释:http://en.wikipedia.org/wiki/Java_annotation

Java规范请求250:http://en.wikipedia.org/wiki/JSR_250

Oracle 注解白皮书:http://www.oracle.com/technetwork/articles/hunter-meta-096020.html

注解API:http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/package-summary.html

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

推荐阅读更多精彩内容