Java中Annotation用法
其他还可以参考的地址
https://www.cnblogs.com/skywang12345/p/3344137.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
Java中Annotation用法的更多相关文章
- JAVA中ArrayList用法
JAVA中ArrayList用法 2011-07-20 15:02:03| 分类: 计算机专业 | 标签:java arraylist用法 |举报|字号 订阅 Java学习过程中做题时 ...
- this在java中的用法
this在java中的用法 1.使用this关键字引用成员变量 作用:解决成员变量与参数或局部变量命名冲突的问题 public class Dog { String name; public Dog( ...
- Java中instanceof用法
java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. 用法:resu ...
- 【本地资源路径&&网络资源路径&&正反斜杠在Java中的用法】
一.概念和用法 左正右反 先来看看转义字符的概念:通过 \ ,?来转变后面字母或符号的含义.意思就是改变字母本身的含义. 以"\"符号为例,JAVA中有很多操作,例如文件操作等,需 ...
- java中annotation
什么是annotation(注解)? java.lang.annotation,接口Annotation.对于Annotation,是Java5的新特性,JDK5引入了Metadata(元数据)很容易 ...
- Java注解(Annotation)用法:利用注解和反射机制指定列名导出数据库数据
闲来没事,想了一个应用的例子:用java如何把数据库的数据根据我们指定的某几列,如第2列,第4列,第6列导出来到Excel里? 写代码也是为了应用的,写好的代码更重要的是在于思考.我自己思考了这个示例 ...
- pat——1017. Queueing at Bank (java中Map用法)
由PAT1017例题展开: Suppose a bank has K windows open for service. There is a yellow line in front of the ...
- Java中Map用法详解
原文地址http://blog.csdn.net/guomutian911/article/details/45771621 原文地址http://blog.csdn.net/sunny2437885 ...
- Java中finalize()用法
Java中finalize() 垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新覆盖这个方法,因为Obj ...
随机推荐
- 【JVM】-NO.110.JVM.1 -【hsdis jitwatch 生成查看汇编代码】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- pycharm的简介
pycharm使用 集成开发环境(IDE,Integrated Development Environment ) VIM #经典的linux下的文本编辑器 Emacs #linux 文本编辑器, 比 ...
- IdentityServer4授权和认证
IdentityServer4 简称ids4 oidc了解:http://www.jessetalk.cn/2018/04/04/oidc-asp-net-core/ 是一个去中心化的网上身份认证系统 ...
- appium环境搭建-运行
appium是测试移动端的测试工具 首先要下载手机模拟器,或者连接真机.我用的夜神模拟器.安装打开它.安装这个有很高的兼容性要求,我也是小白,摸索了三天才弄出来 一.原理如图: 二.需要安装的软件: ...
- Solution about MB STAR C4, MB STAR C5 Update and can not test vehicles problems
Solution about MB Star C4, MB Star C5 Update and can not test vehicles problems 1. Make sure your co ...
- PHP5.6 Dockerfile
FROM centos COPY ["src","/src"] RUN groupadd -g 1000 www \ && useradd -u ...
- 定位bug的基本要求
很多人觉得qa只是负责发现问题,这个实在太狭隘了,现代qa除了发现问题这种基本功外,定位问题,提出解决方案,提出预防方案也是要掌握的技能.这里先说定位问题的要求,定位问题要向深入,前提当然是对功能.产 ...
- ORA-12801/ORA-12853: insufficient memory for PX buffers: current 274880K, max needed 19722240K/ORA-04031解决方法
近日,现场一台服务器在运行时出现下列异常: ORA-12801: error signaled in parallel query server P139 ORA-12853: insufficien ...
- [c/c++] programming之路(19)、数组指针
一.指针运算 #include<stdio.h> #include<stdlib.h> void main0(){ ; int *p=&a; printf());//变 ...
- Bugku-CTF之flag在index里
Day15 flag在index里 http://123.206.87.240:8005/post/