其他还可以参考的地址

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如下:

    1. @Override         限定重写父类的方法
    2. @Deprecated     标示已过时
    3. @SuppressWarnings     抑制编译器警告
  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. /**
  5. * 动物类
  6. */
  7. @SuppressWarnings("unchecked") //压制警告
  8. public class Animal {
  9. List<String> list = new ArrayList<String>();
  10.  
  11. /**
  12. * 动物吃的方法
  13. */
  14. public void eat(){
  15. System.out.println("animal eat method");
  16. }
  17. }
  18.  
  19. /**
  20. * 狗类
  21. */
  22. class Dog extends Animal{
  23. /**
  24. * 规定狗吃的方法继承自动物,就加上该@Override注解
  25. */
  26. @Override
  27. public void eat(){
  28. System.out.println("dog eat method");
  29. }
  30.  
  31. /**
  32. * 定义标识该方法已过期,以后不建议使用该方法
  33. */
  34. @Deprecated
  35. public void go(){
  36.  
  37. }
  38. }
  • 自定义Annotation

  定义新的Annotation类型使用@interface关键字,它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像,如下代码可定义一个简单的Annotation:

  1. public @interface Login {
  2.  
  3. }

   定义了该Annotation之后,就可以在程序任何地方来使用该Annotation,使用Annotation时的语法非常类似于public、final这样的修饰符。通常可用于修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其成员变量指定值,因而Annotation长度可能比较长,所以通常把Annotation另放一行,如下程序所示:

  1. /**
  2. * 定义一个Annotation
  3. */
  4. public @interface Login {
  5.  
  6. }
  7.  
  8. class LoginTest{
  9. /**
  10. * 使用Annotation
  11. */
  12. @Login
       public void login(){
  13.  
  14. }
  15. }

  Annotation不仅可以是这种简单Annotation,Annotation还可以带成员变量,Annotation的成员变量在Annotation定义中以无参数方法的形式声明。其方法名和返回值定义了该成员的名字和类型。如下代码可以定义一个有成员变量的Annotation:

  1. /**
  2. * 定义一个注解
  3. */
  4. public @interface Login {
  5. //定义两个成员变量
  6. String username();
  7. String password();
  8. }

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

  1. /**
  2. * 定义一个注解
  3. */
  4. public @interface Login {
  5. //定义两个成员变量
  6. String username();
  7. String password();
  8. }
  9.  
  10. class LoginTest{
  11. /**
  12. * 使用注解
  13. */
  14. @Login(username="lisi", password="111111")
  15. public void login(){
  16.  
  17. }
  18. }

   我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字,如下代码:

  1. /**
  2. * 定义一个注解
  3. */
  4. public @interface Login {
  5. //定义两个成员变量
  6. //以default为两个成员变量指定初始值
  7. String username() default "zhangsan";
  8. String password() default "123456";
  9. }

  如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值。如下代码:

  1. /**
  2. * 定义一个注解
  3. */
  4. public @interface Login {
  5. //定义两个成员变量
  6. //以default为两个成员变量指定初始值
  7. String username() default "zhangsan";
  8. String password() default "123456";
  9. }
  10.  
  11. class LoginTest{
  12. /**
  13. * 使用注解
       * 因为它的成员变量有默认值,所以可以无须为成员变量指定值,而直接使用默认值
  14. */
  15. @Login
  16. public void login(){
  17.  
  18. }
  19. }

  *根据我们介绍的Annotation是否可以包含成员变量,我们可以把Annotation分为如下两类:

    • 标记Annotation: 一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。如前面介绍的@Override。
    • 元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation。        
  • 提取Annotation的信息

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

  除此之外,Java在java.lang.reflect包下新增了AnnotateElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类(注意以下是类):

    1. Class:类定义。
    2. Constructor:构造器定义。
    3. Field:类的成员变量定义。
    4. Method:类的方法定义。
    5. Package:类的包定义。

  如图所示以Method类为例:

  

  

  java.lang.reflect包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注解才是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

  AnnotatedElement接口是所有程序元素(如Class、Method、Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息:

    1. getAnnotation(Class<T> annotationClass);  //返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。
    2. Annotation[] getAnnotations();      //返回该程序元素上存在的所有注释。
    3. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);      //判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。    

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

  1. //@Retention注解指定Login注解可以保留多久
    //(元注释后面讲)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. //@Target注解指定注解能修饰的目标(只能是方法)
  4. @Target(ElementType.METHOD)
  5. @interface Login{
  6. String username() default "zhangsan";
  7. String password() default "123456";
  8. }
  9.  
  10. public class Test {
  11. public static void main(String[] args) throws Exception{
  12. //1.1通过反射获取info方法类
  13. Method method = Test.class.getMethod("info");
  14. //2.1判断该方法上是否存在@Login注释
  15. boolean annotationPresent = method.isAnnotationPresent(Login.class);
  16. if(annotationPresent){
  17. System.out.println("info方法上存在@Login注释");
  18. }else{
  19. System.out.println("info方法上不存在@Login注释");
  20. }
  21. //3.1获取方法上的所有注释
  22. Annotation[] annotations = method.getAnnotations();
  23. for(Annotation a : annotations){
  24. //如果是@Login注释,则强制转化,并调用username方法,和password方法。
  25. if(a !=null && a instanceof Login){
  26. String username = ((Login)a).username();
  27. String password = ((Login)a).password();
  28. System.out.println("username:" + username);
  29. System.out.println("password:" + password);
  30. }
  31. System.out.println(a);
  32. }
  33. }
  34.  
  35. @Login
  36. @Deprecated
  37. public void info(){}
  38. }
  • 使用Annotation的例子

  下面分别介绍两个使用Annotation的例子,第一个Annotation @Test没有任何成员变量,仅是一个标记Annotation,它的作用是标记哪些方法是可测试的。

  1. //[rɪˈtenʃn]保留
  2. @Retention(RetentionPolicy.RUNTIME)
  3. // [ˈtɑ:gɪt]目标
  4. @Target(ElementType.METHOD)
  5. @interface Test {
  6.  
  7. }
  8.  
  9. class Junit{
  10. @Test
  11. public static void test1(){
  12.  
  13. }
  14.  
  15. public static void test2(){
  16.  
  17. }
  18.  
  19. public static void test3(){
  20.  
  21. }
  22.  
  23. @Test
  24. public static void test4(){
  25.  
  26. }
  27.  
  28. }
  29.  
  30. public class TestTarget{
  31. public static void main(String[] args) throws Exception{
  32. //1.1通过反射获取类
  33. Class<?> forName = Class.forName("com.test.annotation.test1.Junit");
  34. //1.2获取该类自身声明的所有方法
  35. Method[] methods = forName.getDeclaredMethods();
  36. int checkCount = 0; //测试的数量
  37. int uncheckCount = 0; //未测试的数量
  38. for (Method method : methods) {
  39. if(method.isAnnotationPresent(Test.class)){
  40. checkCount++;
  41. }else{
  42. uncheckCount++;
  43. }
  44. }
  45. System.out.println("测试的方法有" + checkCount);
  46. System.out.println("未测试的方法有" + uncheckCount);
  47. }
  48. }

  运行结果如图所示:

    

  上面程序定义了一个标记Test Annotation,定义该Annotation时使用了@Retention和@Target两个系统元注释,其中@Retention注释指定Test注释可以保留多久,@Target注释指定Test注释能修饰的目标(只能是方法)。正如前面提到的,仅仅使用注释来标识程序元素对程序是不会有任何影响的,这也是Java注释的一条重要原则。

  通过这个运行结果可以看出,程序中的@Test起作用了,Junit类里以@Test注释修饰的方法被正常测试了。

  前面介绍的只是一个标记Annotation,程序通过判断该Annotation来决定是否运行指定方法,下面程序通过使用元数据Annotation来简化事件编程,在传统的事件编程中总是需要通过addActionListener方法来为事件源绑定事件监听器,本示例中则通过ActionListenerAnno Annotation来为程序中的按钮绑定监听器。

  1. //(元注释后面讲)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.FIELD) //定义作用在字段上
  4. @Documented
  5. @interface ActionListenerAnno {
  6. //该listener成员变量用于保存监听器实现类
  7. Class<? extends ActionListener> listener();
  8. }
  9.  
  10. public class TestListener {
  11. JFrame jf = new JFrame("测试");
  12. @ActionListenerAnno(listener=OkListener.class)
  13. private JButton ok = new JButton("确认");
  14. @ActionListenerAnno(listener=CancelListener.class)
  15. private JButton cancel = new JButton("取消");
  16. public void init() throws IllegalArgumentException, IllegalAccessException, InstantiationException{
  17. JPanel jp = new JPanel();
  18. jp.add(ok);
  19. jp.add(cancel);
  20. jf.add(jp);
  21. ButtonActionListener.process(this);
  22. jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  23. jf.pack();
  24. jf.setLocationRelativeTo(null);
  25. jf.setVisible(true);
  26. }
  27. public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InstantiationException {
  28. new TestListener().init();
  29. }
  30. }
  31.  
  32. class OkListener implements ActionListener{
  33. @Override
  34. public void actionPerformed(ActionEvent e) {
  35. System.out.println("确认按钮被点击");
  36. JOptionPane.showMessageDialog(null, "确认按钮被点击");
  37. }
  38. }
  39.  
  40. class CancelListener implements ActionListener{
  41. @Override
  42. public void actionPerformed(ActionEvent e) {
  43. System.out.println("取消按钮被点击");
  44. JOptionPane.showMessageDialog(null, "取消按钮被点击");
  45. }
  46.  
  47. }
  48.  
  49. class ButtonActionListener{
  50. public static void process(Object obj) throws IllegalArgumentException, IllegalAccessException, InstantiationException{
  51. Class<? extends Object> clazz = obj.getClass();
  52. Field[] fields = clazz.getDeclaredFields();
  53. for(Field f : fields){
  54. //将指定Field设置成可自由访问的,避免private的Field不能访问
  55. f.setAccessible(true);
  56. //获取指定Field的ActionListenerAnno类型的注解
  57. ActionListenerAnno a = f.getAnnotation(ActionListenerAnno.class);
  58. // 获取成员变量f的值
  59. Object fObj = f.get(obj);
  60. if(a != null && fObj instanceof AbstractButton){
  61. // 获取a注解里的listner元数据(它是一个监听器类)
  62. Class<? extends ActionListener> listenerClazz = a.listener();
  63. // 使用反射来创建listner类的对象
  64. ActionListener al = listenerClazz.newInstance();
  65. AbstractButton ab = (AbstractButton)fObj;
  66. // 为ab按钮添加事件监听器
  67. ab.addActionListener(al);
  68. }
  69. }
  70. }
  71. }

运行结果:

  单击如上图所示窗口的“确定”按钮,将会弹出“确认按钮被点击”的对话框,这表明使用该注释成功地为 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成员变量的值只能是如下三个:

    1. RetentionPolicy.CLASS: 编译器将把注释记录在class文件中。当运行Java程序时,JVM不在保留注释,这是默认值。
    2. RetentionPolicy.RUNTIME: 编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
    3. RetentionPolicy.SOURCE:  注解仅存在于源码中,在class字节码文件中不包含。
  • 使用@Target

  @Target也是用于修饰一个Annotation定义,它用于指定被修饰Annotation能用于修饰那些程序元素。@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类将被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用法的更多相关文章

  1. JAVA中ArrayList用法

    JAVA中ArrayList用法 2011-07-20 15:02:03|  分类: 计算机专业 |  标签:java  arraylist用法  |举报|字号 订阅     Java学习过程中做题时 ...

  2. this在java中的用法

    this在java中的用法 1.使用this关键字引用成员变量 作用:解决成员变量与参数或局部变量命名冲突的问题 public class Dog { String name; public Dog( ...

  3. Java中instanceof用法

    java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. 用法:resu ...

  4. 【本地资源路径&&网络资源路径&&正反斜杠在Java中的用法】

    一.概念和用法 左正右反 先来看看转义字符的概念:通过 \ ,?来转变后面字母或符号的含义.意思就是改变字母本身的含义. 以"\"符号为例,JAVA中有很多操作,例如文件操作等,需 ...

  5. java中annotation

    什么是annotation(注解)? java.lang.annotation,接口Annotation.对于Annotation,是Java5的新特性,JDK5引入了Metadata(元数据)很容易 ...

  6. Java注解(Annotation)用法:利用注解和反射机制指定列名导出数据库数据

    闲来没事,想了一个应用的例子:用java如何把数据库的数据根据我们指定的某几列,如第2列,第4列,第6列导出来到Excel里? 写代码也是为了应用的,写好的代码更重要的是在于思考.我自己思考了这个示例 ...

  7. 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 ...

  8. Java中Map用法详解

    原文地址http://blog.csdn.net/guomutian911/article/details/45771621 原文地址http://blog.csdn.net/sunny2437885 ...

  9. Java中finalize()用法

    Java中finalize()   垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新覆盖这个方法,因为Obj ...

随机推荐

  1. SVProgressHUD提示框IOS

    SVProgressHUD--比MBProgressHUD更好用的 iOS进度提示组件 项目里用到SVProgressHud,感觉背景颜色太丑,因为很久很久以前改过,就想在这个项目里也改下,但是时间过 ...

  2. java框架之SpringCloud(2)-Rest微服务案例

    在上一章节已经对微服务与 SpringCloud 做了介绍,为方便后面学习,下面以 Dept 部门模块为例做一个微服务通用 Demo —— Consumer 消费者(Client) 通过 REST 调 ...

  3. Nginx、Tomcat配置https

    一.Nginx.Tomcat配置https 前提就是已经得到了CA机构颁发的证书 一.合并证书 1.假设证书文件如下 秘钥文件server.key,证书CACertificate-INTERMEDIA ...

  4. Oracle数据库分组排序

    select row_number() over(partition by oea03 order by oea02 desc) num,oea01,oea02,oea03 from oea_file ...

  5. Tensorflow之调试(Debug)及打印变量

    参考资料:https://wookayin.github.io/tensorflow-talk-debugging 几种常用方法: 1.通过Session.run()获取变量的值 2.利用Tensor ...

  6. postgres密码修改

    . 修改PostgreSQL数据库默认用户postgres的密码 PostgreSQL数据库创建一个postgres用户作为数据库的管理员,密码随机,所以需要修改密码,方式如下: 步骤一:登录Post ...

  7. Ubuntu16.04彻底卸载MySQL

    删除mysql的数据文件 sudo rm /var/lib/mysql/ -R 删除mysql的配置文件 sudo rm /etc/mysql/ -R 自动卸载mysql(包括server和clien ...

  8. Python之包管理

    1.setup.py from distutils.core import setup setup(name='Distutils', version='1.0', description='Pyth ...

  9. UI框架搭建DAY1

    分析:UI框架主要是为了用户(使用框架的程序猿)更快捷.方便地开发UI,UI框架的好处还在于解耦,使得程序更具有灵活性. UI框架的核心是窗口的管理,窗口管理的主要任务就是显示窗口和关闭窗口. 因为窗 ...

  10. U盘安装Windows Server 2008 r2失败,改用磁盘安装

    一.使用UltraISO制作系统安装启动U盘提示错误“缺少所需的CD/DVD驱动器设备驱动程序.…………”试了各种方法都不能通过U盘进行安装. 二.直接使用磁盘安装0.使用PE先将磁盘格式化.分区.1 ...