java annotation 用法_JAVA-Annotation的用法

Annotation

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%7D其实是代码里的特殊标记,这些标记可以在

math?formula=%5Ccolor%7Bred%7D%7B%E7%BC%96%E8%AF%91%E3%80%81%E7%B1%BB%E5%8A%A0%E8%BD%BD%E3%80%81%E8%BF%90%E8%A1%8C%7D时被读取,并执行相应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%7D提供了一条为程序元素设置元数据的方法,从某些方面来看,Annotation就像

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E4%BF%AE%E9%A5%B0%E7%AC%A6%7D一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value”对中。

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%7D能被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是:Annotation不能影响程序代码的执行,无论增加、删除Annotation,代码都始终如一地执行。

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E5%A6%82%E6%9E%9C%E5%B8%8C%E6%9C%9B%E8%AE%A9%E7%A8%8B%E5%BA%8F%E4%B8%AD%E7%9A%84Annotation%E8%83%BD%E5%9C%A8%E8%BF%90%E8%A1%8C%E6%97%B6%E8%B5%B7%E4%B8%80%E5%AE%9A%E7%9A%84%E4%BD%9C%E7%94%A8%EF%BC%8C%7D只有通过某种配套的工具对Annotation中的信息进行访问的处理,访问和处理Annotation的工具统称APT(Annotation Processing Tool)。

基本的Annotation

Annotation必须使用工具来处理,

math?formula=%5Ccolor%7Bred%7D%7B%E5%B7%A5%E5%85%B7%E8%B4%9F%E8%B4%A3%E6%8F%90%E5%8F%96Annotation%E9%87%8C%E5%8C%85%E5%90%AB%E7%9A%84%E5%85%83%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%B7%A5%E5%85%B7%E8%BF%98%E4%BC%9A%E6%A0%B9%E6%8D%AE%E8%BF%99%E4%BA%9B%E5%85%83%E6%95%B0%E6%8D%AE%E5%A2%9E%E5%8A%A0%E9%A2%9D%E5%A4%96%E7%9A%84%E5%8A%9F%E8%83%BD%E3%80%82%7D在系统学习新的Annotation语法之前,先看一下Java提供的三个基本Annotation的用法:使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素。

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E4%B8%89%E4%B8%AA%E5%9F%BA%E6%9C%AC%E7%9A%84Annotation%E5%A6%82%E4%B8%8B%EF%BC%9A%7D

1. @Override 限定重写父类的方法

2. @Deprecated 标示已过时

3. @SuppressWarnings 抑制编译器警告

import java.util.ArrayList;

import java.util.List;

/**

* 动物类

*/

@SuppressWarnings("unchecked") //压制警告

public class Animal {

List list = new ArrayList();

/**

* 动物吃的方法

*/

public void eat(){

System.out.println("animal eat method");

}

}

/**

* 狗类

*/

class Dog extends Animal{

/**

* 规定狗吃的方法继承自动物,就加上该@Override注解

*/

@Override

public void eat(){

System.out.println("dog eat method");

}

/**

* 定义标识该方法已过期,以后不建议使用该方法

*/

@Deprecated

public void go(){

}

}

自定义Annotation

定义新的

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%7D类型使用

math?formula=%5Ccolor%7Bred%7D%7B%40interface%7D关键字,它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像,如下代码可定义一个简单的Annotation:

public @interface Login {

}

定义了该Annotation之后,就可以在程序任何地方来使用该Annotation,

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E4%BD%BF%E7%94%A8Annotation%E6%97%B6%E7%9A%84%E8%AF%AD%E6%B3%95%E9%9D%9E%E5%B8%B8%E7%B1%BB%E4%BC%BC%E4%BA%8Epublic%E3%80%81final%E8%BF%99%E6%A0%B7%E7%9A%84%E4%BF%AE%E9%A5%B0%E7%AC%A6%E3%80%82%7D通常可用于修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其成员变量指定值,因而Annotation长度可能比较长,所以

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E9%80%9A%E5%B8%B8%E6%8A%8AAnnotation%E5%8F%A6%E6%94%BE%E4%B8%80%E8%A1%8C%EF%BC%8C%7D如下程序所示:

/**

* 定义一个Annotation

*/

public @interface Login {

}

class LoginTest{

/**

* 使用Annotation

*/

@Login

public void login(){

}

}

Annotation不仅可以是这种简单Annotation,

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%E8%BF%98%E5%8F%AF%E4%BB%A5%E5%B8%A6%E6%88%90%E5%91%98%E5%8F%98%E9%87%8F%EF%BC%8C%7DAnnotation的成员变量在Annotation定义中

math?formula=%5Ccolor%7Bred%7D%7B%E4%BB%A5%E6%97%A0%E5%8F%82%E6%95%B0%E6%96%B9%E6%B3%95%E7%9A%84%E5%BD%A2%E5%BC%8F%E5%A3%B0%E6%98%8E%E3%80%82%7D其方法名和返回值定义了该成员的名字和类型。如下代码可以定义一个有成员变量的Annotation:

/**

* 定义一个注解

*/

public @interface Login {

//定义两个成员变量

String username();

String password();

}

一旦在Annotation里定义了成员变量之后,使用该Annotation时应该为该Annotation的成员变量指定值,如下代码所示:

/**

* 定义一个注解

*/

public @interface Login {

//定义两个成员变量

String username();

String password();

}

class LoginTest{

/**

* 使用注解

*/

@Login(username="lisi", password="111111")

public void login(){

}

}

我们还可以在定义

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%7D的成员变量时为其指定初始值,指定成员变量的初始值可使用

math?formula=%5Ccolor%7Bred%7D%7Bdefault%7D关键字,如下代码:

/**

* 定义一个注解

*/

public @interface Login {

//定义两个成员变量

//以default为两个成员变量指定初始值

String username() default "zhangsan";

String password() default "123456";

}

如果为Annotation的成员变量指定了默认值,使用该Annotation则可以

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E4%B8%8D%E4%B8%BA%E8%BF%99%E4%BA%9B%E6%88%90%E5%91%98%E5%8F%98%E9%87%8F%E6%8C%87%E5%AE%9A%E5%80%BC%EF%BC%8C%E8%80%8C%E6%98%AF%E7%9B%B4%E6%8E%A5%E4%BD%BF%E7%94%A8%E9%BB%98%E8%AE%A4%E5%80%BC%7D。如下代码:

/**

* 定义一个注解

*/

public @interface Login {

//定义两个成员变量

//以default为两个成员变量指定初始值

String username() default "zhangsan";

String password() default "123456";

}

class LoginTest{

/**

* 使用注解

* 因为它的成员变量有默认值,所以可以无须为成员变量指定值,而直接使用默认值

*/

@Login

public void login(){

}

}

根据我们介绍的

math?formula=%5Ccolor%7Bred%7D%7BAnnotation%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8C%85%E5%90%AB%E6%88%90%E5%91%98%E5%8F%98%E9%87%8F%7D,我们可以把Annotation分为如下两类:

math?formula=%5Ccolor%7Bred%7D%7B%E6%A0%87%E8%AE%B0Annotation%7D: 一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。如前面介绍的@Override。

math?formula=%5Ccolor%7Bred%7D%7B%E5%85%83%E6%95%B0%E6%8D%AEAnnotation%7D:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation。

提取Annotation的信息

前面已经提到:Java使用Annotation接口来代表程序元素前面的注释(反射的时候用它来接收注解对象),该接口是所有Annotation类型的父接口。如下图所示是Annotation接口:

d8a1cbd1952a

image

除此之外,Java在

math?formula=%5Ccolor%7Bred%7D%7Bjava.lang.reflect%7D包下新增了

math?formula=%5Ccolor%7Bred%7D%7BAnnotateElement%7D接口,

math?formula=%5Ccolor%7Byellowgreen%7D%7B%E8%AF%A5%E6%8E%A5%E5%8F%A3%E4%BB%A3%E8%A1%A8%E7%A8%8B%E5%BA%8F%E4%B8%AD%E5%8F%AF%E4%BB%A5%E6%8E%A5%E5%8F%97%E6%B3%A8%E9%87%8A%E7%9A%84%E7%A8%8B%E5%BA%8F%E5%85%83%E7%B4%A0%7D,该接口主要有如下几个

math?formula=%5Ccolor%7Borange%7D%7B%E5%AE%9E%E7%8E%B0%E7%B1%BB%EF%BC%88%E6%B3%A8%E6%84%8F%E4%BB%A5%E4%B8%8B%E6%98%AF%E7%B1%BB%EF%BC%89%7D

1. Class:类定义。

2. Constructor:构造器定义。

3. Field:类的成员变量定义。

4. Method:类的方法定义。

5. Package:类的包定义。

如图所示以Method类为例:

d8a1cbd1952a

image

d8a1cbd1952a

image

math?formula=%5Ccolor%7Bred%7D%7Bjava.lang.reflect%7D 包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包提供的反射API扩充了

math?formula=%5Ccolor%7Bred%7D%7B%E8%AF%BB%E5%8F%96%E8%BF%90%E8%A1%8C%7D时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注解才是

math?formula=%5Ccolor%7Bred%7D%7B%E8%BF%90%E8%A1%8C%E6%97%B6%7D可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

math?formula=%5Ccolor%7Bred%7D%7BAnnotatedElement%7D接口是所有程序元素(如Class、Method、Constructor)的

math?formula=%5Ccolor%7Bred%7D%7B%E7%88%B6%E6%8E%A5%E5%8F%A3%7D,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息:

1. getAnnotation(Class annotationClass); //返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。

2. Annotation[] getAnnotations(); //返回该程序元素上存在的所有注释。

3. boolean isAnnotationPresent(Class extends Annotation> annotationClass); //判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。

下面程序片段用于获取Test类的info方法里的所有注释,并将这些注释打印出来:

//[rɪˈtenʃn]保留

@Retention(RetentionPolicy.RUNTIME)

// [ˈtɑ:gɪt]目标

@Target(ElementType.METHOD)

@interface Test {

}

class Junit{

@Test

public static void test1(){

}

public static void test2(){

}

public static void test3(){

}

@Test

public static void test4(){

}

}

public class TestTarget{

public static void main(String[] args) throws Exception{

//1.1通过反射获取类

Class> forName = Class.forName("com.test.annotation.test1.Junit");

//1.2获取该类自身声明的所有方法

Method[] methods = forName.getDeclaredMethods();

int checkCount = 0; //测试的数量

int uncheckCount = 0; //未测试的数量

for (Method method : methods) {

if(method.isAnnotationPresent(Test.class)){

checkCount++;

}else{

uncheckCount++;

}

}

System.out.println("测试的方法有" + checkCount);

System.out.println("未测试的方法有" + uncheckCount);

}

}

运行结果如图所示:

d8a1cbd1952a

image

上面程序定义了一个标记Test Annotation,定义该Annotation时使用了@Retention和@Target两个系统元注释,其中@Retention注释指定Test注释可以保留多久,@Target注释指定Test注释能修饰的目标(只能是方法)。正如前面提到的,

math?formula=%5Ccolor%7Bred%7D%7B%E4%BB%85%E4%BB%85%E4%BD%BF%E7%94%A8%E6%B3%A8%E9%87%8A%E6%9D%A5%E6%A0%87%E8%AF%86%E7%A8%8B%E5%BA%8F%E5%85%83%E7%B4%A0%E5%AF%B9%E7%A8%8B%E5%BA%8F%E6%98%AF%E4%B8%8D%E4%BC%9A%E6%9C%89%E4%BB%BB%E4%BD%95%E5%BD%B1%E5%93%8D%E7%9A%84%EF%BC%8C%E8%BF%99%E4%B9%9F%E6%98%AFJava%E6%B3%A8%E9%87%8A%E7%9A%84%E4%B8%80%E6%9D%A1%E9%87%8D%E8%A6%81%E5%8E%9F%E5%88%99%E3%80%82%7D

通过这个运行结果可以看出,程序中的

math?formula=%5Ccolor%7Bred%7D%7B%40Test%7D起作用了,Junit类里以@Test注释修饰的方法被正常测试了。

前面介绍的只是一个标记Annotation,程序通过判断该Annotation来决定是否运行指定方法,下面程序通过

math?formula=%5Ccolor%7Bred%7D%7B%E4%BD%BF%E7%94%A8%E5%85%83%E6%95%B0%E6%8D%AEAnnotation%E6%9D%A5%E7%AE%80%E5%8C%96%E4%BA%8B%E4%BB%B6%E7%BC%96%E7%A8%8B%7D,在传统的事件编程中总是需要通过addActionListener方法来为事件源绑定事件监听器,本示例中则通过ActionListenerAnno Annotation来为程序中的按钮绑定监听器。

//(元注释后面讲)

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD) //定义作用在字段上

@Documented

@interface ActionListenerAnno {

//该listener成员变量用于保存监听器实现类

Class extends ActionListener> listener();

}

public class TestListener {

JFrame jf = new JFrame("测试");

@ActionListenerAnno(listener=OkListener.class)

private JButton ok = new JButton("确认");

@ActionListenerAnno(listener=CancelListener.class)

private JButton cancel = new JButton("取消");

public void init() throws IllegalArgumentException, IllegalAccessException, InstantiationException{

JPanel jp = new JPanel();

jp.add(ok);

jp.add(cancel);

jf.add(jp);

ButtonActionListener.process(this);

jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

jf.pack();

jf.setLocationRelativeTo(null);

jf.setVisible(true);

}

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InstantiationException {

new TestListener().init();

}

}

class OkListener implements ActionListener{

@Override

public void actionPerformed(ActionEvent e) {

System.out.println("确认按钮被点击");

JOptionPane.showMessageDialog(null, "确认按钮被点击");

}

}

class CancelListener implements ActionListener{

@Override

public void actionPerformed(ActionEvent e) {

System.out.println("取消按钮被点击");

JOptionPane.showMessageDialog(null, "取消按钮被点击");

}

}

class ButtonActionListener{

public static void process(Object obj) throws IllegalArgumentException, IllegalAccessException, InstantiationException{

Class extends Object> clazz = obj.getClass();

Field[] fields = clazz.getDeclaredFields();

for(Field f : fields){

//将指定Field设置成可自由访问的,避免private的Field不能访问

f.setAccessible(true);

//获取指定Field的ActionListenerAnno类型的注解

ActionListenerAnno a = f.getAnnotation(ActionListenerAnno.class);

// 获取成员变量f的值

Object fObj = f.get(obj);

if(a != null && fObj instanceof AbstractButton){

// 获取a注解里的listner元数据(它是一个监听器类)

Class extends ActionListener> listenerClazz = a.listener();

// 使用反射来创建listner类的对象

ActionListener al = listenerClazz.newInstance();

AbstractButton ab = (AbstractButton)fObj;

// 为ab按钮添加事件监听器

ab.addActionListener(al);

}

}

}

}

运行结果:

d8a1cbd1952a

image

单击如上图所示窗口的“确定”按钮,将会弹出“确认按钮被点击”的对话框,这表明使用该注释成功地为 ok、cancel两个按钮绑定了事件监听器。

JDK的元Annotation

JDK除了在java.lang 下提供了

math?formula=%5Ccolor%7Bred%7D%7B3%E4%B8%AA%E5%9F%BA%E6%9C%ACAnnotation%7D之外,还在java.lang.annotation包下提供了

math?formula=%5Ccolor%7Bred%7D%7B%E5%9B%9B%E4%B8%AAMeta%20Annotation(%E5%85%83Annotation)%EF%BC%8C%7D这四个Annotation都是用于修饰其他Annotation定义。

使用@Retention

@Retention只能用于修饰一个Annotation定义,

math?formula=%5Ccolor%7Bred%7D%7B%E7%94%A8%E4%BA%8E%E6%8C%87%E5%AE%9A%E8%AF%A5Annotation%E5%8F%AF%E4%BB%A5%E4%BF%9D%E7%95%99%E5%A4%9A%E9%95%BF%E6%97%B6%E9%97%B4%EF%BC%8C%7D@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。

value成员变量的值只能是如下三个:

1. RetentionPolicy.CLASS: 编译器将把注释记录在class文件中。当运行Java程序时,JVM不在保留注释,这是默认值。

2. RetentionPolicy.RUNTIME: 编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。

3. RetentionPolicy.SOURCE: 注解仅存在于源码中,在class字节码文件中不包含。

使用@Target

@Target也是用于修饰一个Annotation定义,

math?formula=%5Ccolor%7Bred%7D%7B%E5%AE%83%E7%94%A8%E4%BA%8E%E6%8C%87%E5%AE%9A%E8%A2%AB%E4%BF%AE%E9%A5%B0Annotation%E8%83%BD%E7%94%A8%E4%BA%8E%E4%BF%AE%E9%A5%B0%E9%82%A3%E4%BA%9B%E7%A8%8B%E5%BA%8F%E5%85%83%E7%B4%A0%7D。@Target Annotation也包含一个名为value的成员变量,该成员变量只能是如下几个:

1. ElementType.ANNOTATION_TYPE: 指定该策略的Annotation只能修饰Annotation。

2. ElementType.CONSTRUCTOR: 指定该策略的Annotation能修饰构造器。

3. ElementType.FIELD: 指定该策略的Annotation只能修饰成员变量。

4. ElementType.LOCAL_VARIABLE: 指定该策略的Annotation只能修饰局部变量。

5. ElementType.METHOD: 指定该策略的Annotation只能修饰方法。

6. ElementType.PACKAGE: 指定该策略的Annotation只能修饰包定义。

7. ElementType.PARAMETER: 指定该策略的Annotation可以修饰参数。

8. ElementType.TYPE: 指定该策略的Annotation可以修饰类、接口(包括注释类型)或枚举定义。

使用@Documented

@Documented用于指定该元Annotation修饰的Annotation类将

math?formula=%5Ccolor%7Bred%7D%7B%E8%A2%ABjavadoc%E5%B7%A5%E5%85%B7%E6%8F%90%E5%8F%96%E6%88%90%E6%96%87%E6%A1%A3%EF%BC%8C%7D如果定义Annotation类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。

使用@Inherited

@Inherited 元 Annotation指定被它修饰的Annotation将

math?formula=%5Ccolor%7Bred%7D%7B%E5%85%B7%E6%9C%89%E7%BB%A7%E6%89%BF%E6%80%A7%7D:如果某个类使用了A Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则

math?formula=%5Ccolor%7Bred%7D%7B%E5%85%B6%E5%AD%90%E7%B1%BB%E5%B0%86%E8%87%AA%E5%8A%A8%E5%85%B7%E6%9C%89A%E6%B3%A8%E9%87%8A%E3%80%82%7D

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值