• Java中的Annotation详解


    一、三个基本Annotation

    Annotation简介

    从JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注解,也有地方称之为注释),其实说白了就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。

    Annotation提供了一条为程序元素设置元数据的方法,从某些方面来看,Annotation就像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value”对中。Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象然后通过Annotation对象取得注释里的元数据。

    Annotation能被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是:Annotation不能影响程序代码执行,无论增加、删除Annotation,代码都始终如一地执行。如果希望让程序中的Annotation能在运行时起一定作用,只有通过某种配套的工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称APT(AnnotationProcessing Tool)。

    Java中提供三个基本的Annotation

    限定重写父类方法:@Override

    @Override就是用来指定方法重写的,它可以强调一个子类必须要覆盖父类的方法。如下程序中使用@Override指定子类Apple的info方法必须重写父类方法

    1. public class Fruit  
    2. {  
    3.     public void info()  
    4.     {   
    5.         System.out.println("水果的info方法。。。");   
    6.     }  
    7. }   
    8.   
    9. class Apple extends Fruit  
    10. {  
    11.     //使用@Override指定下面方法必须重写父类方法  
    12.     @Override   
    13. //  public void info()  
    14.     public void inf0()  
    15.     {   
    16.         System.out.println("苹果重写水果的info方法。。。");   
    17.     }  
    18. }  

    如果重写的方法名以及参数与父类一直那么看不出@Override什么作用,但是如果重写的方法与父类的方法不一致,那么程序将在编译阶段出错。也就是说这个注解的作用就是帮助我们避免一些低级错误。

    标示已过时:@Deprecated

    @Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。如下程序指定Apple类中的info方法已过时,其他程序中使用Apple类的info方法时编译器将会给出警告。

    1. class Apple  
    2. {  
    3.     //定义info方法已过时  
    4.     @Deprecated  
    5.     public void info()  
    6.     {  
    7.         System.out.println("Apple的info方法");  
    8.     }  
    9. }  
    10. public class DeprecatedTest  
    11. {  
    12.     public static void main(String[] args)   
    13.     {  
    14.         //下面使用info方法时将会被编译器警告  
    15.         new Apple().info();  
    16.     }     
    17. }  

    在eclipse中标注此注解的方法会被删除线标记出来,以引起编程人员的注意。

    抑制编译器警告:@SuppressWarnings

    @SuppressWarnings指示被Annotation标识的程序元素(以及在该程序元素中的所有子元素)取消显示指定的编译器警告。@SuppressWarnings会一直作用于该程序元素的所有子元素,例如使用@SuppressWarnings标识的一个类来取消显示某个编译器警告,同时又标识该类里某个方法取消显示另一个编译器警告,那么在此方法中同时取消显示这两个编译器警告。

    通常情况下如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用@SuppressWarnings这个注解,下面程序取消了没有使用泛型的编译器警告。

    1. @SuppressWarnings(value="unchecked")  
    2. public class SuppressWarningsTest  
    3. {  
    4.     public static void main(String[] args)   
    5.     {  
    6.         List<String> myList = new ArrayList();          
    7.     }     
    8. }  

    正如程序中所看到的,当我们使用此注解来关闭编译器警告时,一定需要在括号里使用name=value对来为该注解成员变量设置值。

    以上就是对注解的一个简单的介绍


    二、Annotation的工作原理

    本文将介绍Annotation的原理,以及如何自定义Annotation,并利用Annotation来完成一些实际的功能。

    定义Annotation

    定义新的Annotation类型使用@interface关键字,这从一定意义上说明Annotation和接口的定义是有相似之处的。如下代码定义了一个Annotation

    1.     //使用DK的元数据Annotation:Retention  
    2.     @Retention(RetentionPolicy.RUNTIME)  
    3.     //使用JDK的元数据Annotation:Target  
    4.     @Target(ElementType.METHOD)  
    5.     //定义一个标记注释,不包含任何成员变量,即不可传入元数据  
    6. public @interface Testable{  
    7.       
    8. }  

    使用Annotation

    定义了Annotation之后就可以在程序的任何地方使用该Annotation,对于Annotation的使用和public、final这样的修饰符很像,通常可以修饰类、方法、变量、接口等定义(默认情况下,Annotation用于修饰任何程序元素)。如下代码,就是使用了上面的Annotation修饰方法。

    1. public class MyTest {  
    2.     //使用@Testable标记注释指定该方法是可测试的  
    3.     @Testable  
    4.     public static void m1(){  
    5.           
    6.     }  
    7.     public static void m2(){  
    8.           
    9.     }  
    10.     //使用@Testable标记注释指定该方法是可测试的  
    11.     @Testable  
    12.     public static void m3(){  
    13.         throw new RuntimeException("Boom");  
    14.     }  
    15.     public static void m4(){  
    16.           
    17.     }  
    18.     //使用@Testable标记注释指定该方法是可测试的  
    19.     @Testable  
    20.     public static void m5(){  
    21.           
    22.     }  
    23.     public static void m6(){  
    24.           
    25.     }  
    26.     //使用@Testable标记注释指定该方法是可测试的  
    27.     @Testable  
    28.     public static void m7(){  
    29.         throw new RuntimeException("Crash");  
    30.     }  
    31.     public static void m8(){  
    32.           
    33.     }  
    34. }  

    提取Annotation

    一个类或者一个方法使用Annotation修饰之后需要通过反射来提取Annotation的信息。需要注意当一个Annotation类型被定义为运行时Annotation后该注释才是运行时可见的,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。下面代码就是将Annotation信息提取出来,根据这些信息进行相应的操作。在这里是如果某个方法被标记为可以测试的则调用这个方法。

    1. public class TestProcessor {  
    2.     public static void process(String clazz)throws ClassNotFoundException{  
    3.         int passed=0;  
    4.         int failed=0;  
    5.         //遍历obj对象的所有方法  
    6.         for(Method m :Class.forName(clazz).getMethods()){  
    7.             //如果包含@Testable标记注释  
    8.             if(m.isAnnotationPresent(Testable.class)){  
    9.                 try{  
    10.                     //调用m方法  
    11.                     m.invoke(null);  
    12.                     //passed加1  
    13.                     passed++;  
    14.                 }catch (Exception ex) {  
    15.                     System.out.printf("方法"+m+"运行失败,异常"+ex.getCause()+" ");  
    16.                     failed++;  
    17.                 }  
    18.             }  
    19.         }  
    20.         //统计测试结果  
    21.         System.out.println("共运行了:"+(passed+failed)+"个方法,其中: 失败了:"+failed+",个, 成功了:"+passed+"个! ");  
    22.     }  
    23. }  

    其中程序的主程序如下

    1. public class RunTests {  
    2.     public static void main(String[] args) throws Exception{  
    3.         //处理Mytest类  
    4.         TestProcessor.process("annotation.MyTest");  
    5.     }  
    6. }  

    运行结果如下

    工作原理

    上面仅仅是一个简单地Annotation的使用,更复杂一些的Annotation与上面的这个例子原理是一样的,只不过相应的逻辑判断以及涉及到的Annotation中的内容较为复杂而已。但是核心的部分就是借助一个操作类通过反射的方式提取Annotation信息,进行操作,简而言之:无反射,不注释(Annotation)


    原文链接:http://blog.csdn.net/beijiguangyong/article/details/8685452


  • 相关阅读:
    时间戳
    MD5加密、字节与字符串转换、对ToString("X2 ")的理解
    JWT(JSON Web Token)简介
    Entity Framework 通过主键查询提高效率
    C# switch语句与枚举类型
    对象映射库【AutoMapper】所支持场景
    关于EF框架EntityState的几种状态
    EF底层操作注意点、批量操作、更新
    linq:求分组后的每组最大值、最小值、平均值等、Aggregate聚合函数
    Flask——Request(2)
  • 原文地址:https://www.cnblogs.com/xieyuan/p/3787240.html
Copyright © 2020-2023  润新知