Java - Annotation使用
本文转载于(这个写的很好):https://www.cnblogs.com/be-forward-to-help-others/p/6846821.html
Annotation
Annotation其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
Annotation提供了一条为程序元素设置元数据的方法,从某些方面来看,Annotation就像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value”对中。
Annotation能被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是:Annotation不能影响程序代码的执行,无论增加、删除Annotation,代码都始终如一地执行。如果希望让程序中的Annotation能在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问的处理,访问和处理Annotation的工具统称APT(Annotation Processing Tool)。
基本的Annotation
Annotation必须使用工具来处理,工具负责提取Annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。在系统学习新的Annotation语法之前,先看一下Java提供的三个基本Annotation的用法:使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素。
三个基本的Annotation如下:
- @Override 限定重写父类的方法
- @Deprecated 标示已过时
- @SuppressWarnings 抑制编译器警告
import java.util.ArrayList;import java.util.List;
/**
* 动物类
*/
@SuppressWarnings("unchecked") //压制警告
public class Animal {
List<String> list = new ArrayList<String>();
/**
* 动物吃的方法
*/
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
定义新的Annotation类型使用@interface关键字,它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像,如下代码可定义一个简单的Annotation:
public @interface Login {}
定义了该Annotation之后,就可以在程序任何地方来使用该Annotation,使用Annotation时的语法非常类似于public、final这样的修饰符。通常可用于修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其成员变量指定值,因而Annotation长度可能比较长,所以通常把Annotation另放一行,如下程序所示:
/*** 定义一个Annotation
*/
public @interface Login {
}
class LoginTest{
/**
* 使用Annotation
*/
@Login
public void login(){}
}
Annotation不仅可以是这种简单Annotation,Annotation还可以带成员变量,Annotation的成员变量在Annotation定义中以无参数方法的形式声明。其方法名和返回值定义了该成员的名字和类型。如下代码可以定义一个有成员变量的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(){
}
}
我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字,如下代码:
/*** 定义一个注解
*/
public @interface Login {
//定义两个成员变量
//以default为两个成员变量指定初始值
String username() default "zhangsan";
String password() default "123456";
}
如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值。如下代码:
/*** 定义一个注解
*/
public @interface Login {
//定义两个成员变量
//以default为两个成员变量指定初始值
String username() default "zhangsan";
String password() default "123456";
}
class LoginTest{
/**
* 使用注解
* 因为它的成员变量有默认值,所以可以无须为成员变量指定值,而直接使用默认值*/
@Login
public void login(){
}
}
*根据我们介绍的Annotation是否可以包含成员变量,我们可以把Annotation分为如下两类:
标记Annotation: 一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。如前面介绍的@Override。
元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation。
提取Annotation的信息
前面已经提到:Java使用Annotation接口来代表程序元素前面的注释(反射的时候用它来接收注解对象),该接口是所有Annotation类型的父接口。如下图所示是Annotation接口:
除此之外,Java在java.lang.reflect包下新增了AnnotateElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类(注意以下是类):
- Class:类定义。
- Constructor:构造器定义。
- Field:类的成员变量定义。
- Method:类的方法定义。
- Package:类的包定义。
如图所示以Method类为例:
java.lang.reflect包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注解才是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement接口是所有程序元素(如Class、Method、Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息:
- getAnnotation(Class<T> annotationClass); //返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。
- Annotation[] getAnnotations(); //返回该程序元素上存在的所有注释。
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); //判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。
下面程序片段用于获取Test类的info方法里的所有注释,并将这些注释打印出来:
//@Retention注解指定Login注解可以保留多久
//(元注释后面讲)@Retention(RetentionPolicy.RUNTIME)
//@Target注解指定注解能修饰的目标(只能是方法)
@Target(ElementType.METHOD)
@interface Login{
String username() default "zhangsan";
String password() default "123456";
}
public class Test {
public static void main(String[] args) throws Exception{
//1.1通过反射获取info方法类
Method method = Test.class.getMethod("info");
//2.1判断该方法上是否存在@Login注释
boolean annotationPresent = method.isAnnotationPresent(Login.class);
if(annotationPresent){
System.out.println("info方法上存在@Login注释");
}else{
System.out.println("info方法上不存在@Login注释");
}
//3.1获取方法上的所有注释
Annotation[] annotations = method.getAnnotations();
for(Annotation a : annotations){
//如果是@Login注释,则强制转化,并调用username方法,和password方法。
if(a !=null && a instanceof Login){
String username = ((Login)a).username();
String password = ((Login)a).password();
System.out.println("username:" + username);
System.out.println("password:" + password);
}
System.out.println(a);
}
}
@Login
@Deprecated
public void info(){}
}
使用Annotation的例子
下面分别介绍两个使用Annotation的例子,第一个Annotation @Test没有任何成员变量,仅是一个标记Annotation,它的作用是标记哪些方法是可测试的。
//[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);
}
}
运行结果如图所示:
上面程序定义了一个标记Test Annotation,定义该Annotation时使用了@Retention和@Target两个系统元注释,其中@Retention注释指定Test注释可以保留多久,@Target注释指定Test注释能修饰的目标(只能是方法)。正如前面提到的,仅仅使用注释来标识程序元素对程序是不会有任何影响的,这也是Java注释的一条重要原则。
通过这个运行结果可以看出,程序中的@Test起作用了,Junit类里以@Test注释修饰的方法被正常测试了。
前面介绍的只是一个标记Annotation,程序通过判断该Annotation来决定是否运行指定方法,下面程序通过使用元数据Annotation来简化事件编程,在传统的事件编程中总是需要通过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);
}
}
}
}
运行结果:
单击如上图所示窗口的“确定”按钮,将会弹出“确认按钮被点击”的对话框,这表明使用该注释成功地为 ok、cancel两个按钮绑定了事件监听器。
JDK的元Annotation
JDK除了在java.lang 下提供了3个基本Annotation之外,还在java.lang.annotation包下提供了四个Meta Annotation(元Annotation),这四个Annotation都是用于修饰其他Annotation定义。
使用@Retention
@Retention只能用于修饰一个Annotation定义,用于指定该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。
value成员变量的值只能是如下三个:
RetentionPolicy.CLASS: 编译器将把注释记录在class文件中。当运行Java程序时,JVM不在保留注释,这是默认值。
RetentionPolicy.RUNTIME: 编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
RetentionPolicy.SOURCE: 注解仅存在于源码中,在class字节码文件中不包含。
使用@Target
@Target也是用于修饰一个Annotation定义,它用于指定被修饰Annotation能用于修饰那些程序元素。@Target Annotation也包含一个名为value的成员变量,该成员变量只能是如下几个:
- ElementType.ANNOTATION_TYPE: 指定该策略的Annotation只能修饰Annotation。
- ElementType.CONSTRUCTOR: 指定该策略的Annotation能修饰构造器。
- ElementType.FIELD: 指定该策略的Annotation只能修饰成员变量。
- ElementType.LOCAL_VARIABLE: 指定该策略的Annotation只能修饰局部变量。
- ElementType.METHOD: 指定该策略的Annotation只能修饰方法。
- ElementType.PACKAGE: 指定该策略的Annotation只能修饰包定义。
- ElementType.PARAMETER: 指定该策略的Annotation可以修饰参数。
- ElementType.TYPE: 指定该策略的Annotation可以修饰类、接口(包括注释类型)或枚举定义。
使用@Documented
@Documented用于指定该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。
使用@Inherited
@Inherited 元 Annotation指定被它修饰的Annotation将具有继承性:如果某个类使用了A Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动具有A注释。
本文转载于(这个写的很好):https://www.cnblogs.com/be-forward-to-help-others/p/6846821.html
Annotation
Annotation其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
Annotation提供了一条为程序元素设置元数据的方法,从某些方面来看,Annotation就像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value”对中。
Annotation能被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是:Annotation不能影响程序代码的执行,无论增加、删除Annotation,代码都始终如一地执行。如果希望让程序中的Annotation能在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问的处理,访问和处理Annotation的工具统称APT(Annotation Processing Tool)。
基本的Annotation
Annotation必须使用工具来处理,工具负责提取Annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。在系统学习新的Annotation语法之前,先看一下Java提供的三个基本Annotation的用法:使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素。
三个基本的Annotation如下:
- @Override 限定重写父类的方法
- @Deprecated 标示已过时
- @SuppressWarnings 抑制编译器警告
import java.util.ArrayList;import java.util.List;
/**
* 动物类
*/
@SuppressWarnings("unchecked") //压制警告
public class Animal {
List<String> list = new ArrayList<String>();
/**
* 动物吃的方法
*/
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
定义新的Annotation类型使用@interface关键字,它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像,如下代码可定义一个简单的Annotation:
public @interface Login {}
定义了该Annotation之后,就可以在程序任何地方来使用该Annotation,使用Annotation时的语法非常类似于public、final这样的修饰符。通常可用于修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其成员变量指定值,因而Annotation长度可能比较长,所以通常把Annotation另放一行,如下程序所示:
/*** 定义一个Annotation
*/
public @interface Login {
}
class LoginTest{
/**
* 使用Annotation
*/
@Login
public void login(){}
}
Annotation不仅可以是这种简单Annotation,Annotation还可以带成员变量,Annotation的成员变量在Annotation定义中以无参数方法的形式声明。其方法名和返回值定义了该成员的名字和类型。如下代码可以定义一个有成员变量的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(){
}
}
我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字,如下代码:
/*** 定义一个注解
*/
public @interface Login {
//定义两个成员变量
//以default为两个成员变量指定初始值
String username() default "zhangsan";
String password() default "123456";
}
如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值。如下代码:
/*** 定义一个注解
*/
public @interface Login {
//定义两个成员变量
//以default为两个成员变量指定初始值
String username() default "zhangsan";
String password() default "123456";
}
class LoginTest{
/**
* 使用注解
* 因为它的成员变量有默认值,所以可以无须为成员变量指定值,而直接使用默认值*/
@Login
public void login(){
}
}
*根据我们介绍的Annotation是否可以包含成员变量,我们可以把Annotation分为如下两类:
标记Annotation: 一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。如前面介绍的@Override。
元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation。
提取Annotation的信息
前面已经提到:Java使用Annotation接口来代表程序元素前面的注释(反射的时候用它来接收注解对象),该接口是所有Annotation类型的父接口。如下图所示是Annotation接口:
除此之外,Java在java.lang.reflect包下新增了AnnotateElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类(注意以下是类):
- Class:类定义。
- Constructor:构造器定义。
- Field:类的成员变量定义。
- Method:类的方法定义。
- Package:类的包定义。
如图所示以Method类为例:
java.lang.reflect包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注解才是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement接口是所有程序元素(如Class、Method、Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息:
- getAnnotation(Class<T> annotationClass); //返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。
- Annotation[] getAnnotations(); //返回该程序元素上存在的所有注释。
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); //判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。
下面程序片段用于获取Test类的info方法里的所有注释,并将这些注释打印出来:
//@Retention注解指定Login注解可以保留多久
//(元注释后面讲)@Retention(RetentionPolicy.RUNTIME)
//@Target注解指定注解能修饰的目标(只能是方法)
@Target(ElementType.METHOD)
@interface Login{
String username() default "zhangsan";
String password() default "123456";
}
public class Test {
public static void main(String[] args) throws Exception{
//1.1通过反射获取info方法类
Method method = Test.class.getMethod("info");
//2.1判断该方法上是否存在@Login注释
boolean annotationPresent = method.isAnnotationPresent(Login.class);
if(annotationPresent){
System.out.println("info方法上存在@Login注释");
}else{
System.out.println("info方法上不存在@Login注释");
}
//3.1获取方法上的所有注释
Annotation[] annotations = method.getAnnotations();
for(Annotation a : annotations){
//如果是@Login注释,则强制转化,并调用username方法,和password方法。
if(a !=null && a instanceof Login){
String username = ((Login)a).username();
String password = ((Login)a).password();
System.out.println("username:" + username);
System.out.println("password:" + password);
}
System.out.println(a);
}
}
@Login
@Deprecated
public void info(){}
}
使用Annotation的例子
下面分别介绍两个使用Annotation的例子,第一个Annotation @Test没有任何成员变量,仅是一个标记Annotation,它的作用是标记哪些方法是可测试的。
//[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);
}
}
运行结果如图所示:
上面程序定义了一个标记Test Annotation,定义该Annotation时使用了@Retention和@Target两个系统元注释,其中@Retention注释指定Test注释可以保留多久,@Target注释指定Test注释能修饰的目标(只能是方法)。正如前面提到的,仅仅使用注释来标识程序元素对程序是不会有任何影响的,这也是Java注释的一条重要原则。
通过这个运行结果可以看出,程序中的@Test起作用了,Junit类里以@Test注释修饰的方法被正常测试了。
前面介绍的只是一个标记Annotation,程序通过判断该Annotation来决定是否运行指定方法,下面程序通过使用元数据Annotation来简化事件编程,在传统的事件编程中总是需要通过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);
}
}
}
}
运行结果:
单击如上图所示窗口的“确定”按钮,将会弹出“确认按钮被点击”的对话框,这表明使用该注释成功地为 ok、cancel两个按钮绑定了事件监听器。
JDK的元Annotation
JDK除了在java.lang 下提供了3个基本Annotation之外,还在java.lang.annotation包下提供了四个Meta Annotation(元Annotation),这四个Annotation都是用于修饰其他Annotation定义。
使用@Retention
@Retention只能用于修饰一个Annotation定义,用于指定该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。
value成员变量的值只能是如下三个:
RetentionPolicy.CLASS: 编译器将把注释记录在class文件中。当运行Java程序时,JVM不在保留注释,这是默认值。
RetentionPolicy.RUNTIME: 编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
RetentionPolicy.SOURCE: 注解仅存在于源码中,在class字节码文件中不包含。
使用@Target
@Target也是用于修饰一个Annotation定义,它用于指定被修饰Annotation能用于修饰那些程序元素。@Target Annotation也包含一个名为value的成员变量,该成员变量只能是如下几个:
- ElementType.ANNOTATION_TYPE: 指定该策略的Annotation只能修饰Annotation。
- ElementType.CONSTRUCTOR: 指定该策略的Annotation能修饰构造器。
- ElementType.FIELD: 指定该策略的Annotation只能修饰成员变量。
- ElementType.LOCAL_VARIABLE: 指定该策略的Annotation只能修饰局部变量。
- ElementType.METHOD: 指定该策略的Annotation只能修饰方法。
- ElementType.PACKAGE: 指定该策略的Annotation只能修饰包定义。
- ElementType.PARAMETER: 指定该策略的Annotation可以修饰参数。
- ElementType.TYPE: 指定该策略的Annotation可以修饰类、接口(包括注释类型)或枚举定义。
使用@Documented
@Documented用于指定该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。
使用@Inherited
@Inherited 元 Annotation指定被它修饰的Annotation将具有继承性:如果某个类使用了A Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动具有A注释。
以上是 Java - Annotation使用 的全部内容, 来源链接: utcz.com/z/394171.html