zoukankan      html  css  js  c++  java
  • Java注解 Annotation

    Java注解 Annotation

    Annotation的由来

      从JDK5.0发布以来,5.0平台提供了一个正式的annotation功能:允许开发者定义、使用自己的annotation类型。

      此功能由一个定义annotation类型的语法和一个描述annotation声明的语法,读取annotation的API,一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成。

    Annotation工作方式

      Annotation并不直接影响代码语义,但是它能够被看作类似程序的工具或者类库,它会反过来对正在运行的程序语义有所影响。

      Annotation可以从源文件、class文件、或者以在运行时反射的多种方式被读取。

     

    JDK中内置的注解

      Java注解Annotation:

      Override注解(@Override)表示子类要重写父类的对应方法。

      Deprecated注解(@Deprecated)表示方法是不建议被使用的。

      SuppressWarnings注解(如:@SuppressWarnings("unchecked"))表示抑制警告。

      括号中的参数(一个字符串数组)表示要压制的警告类型。单个字符串的时候可以用也可以不用花括号,但是多个字符串的时候需要用花括号包起来表示,

      比如:    

      @SuppressWarnings({"unchecked","deprecation"})

      如果注解一个类去压制一种警告,再注解类中的方法取压制另一种警告,则方法中会同时压制这两种警告。

     

    Annotation的定义方式

      自己定义注解:

      新建->Annotation.

      可以看到注解定义的形式与interface类似,不同的是在interface关键字前面加了一个@符号。

      自定义注解:当注解中的属性名为value时,在对其赋值时可以不指定属性的名称而直接写上属性值即可;除了value以外的其他值都需要使用name=value这种显式赋值方式,即明确指定给谁赋值。

      程序示例1  

    注解程序示例1——自定义注解
    public @interface AnnotationTest
    {
    
        String value() default "hello";
        //不加()就出错了
        //属性名字叫作value时可以不加名字直接赋值
        //属性名字不叫作value时给属性赋值必须显式指定名字
        //可以通过default关键字设置默认值
    }
    注解程序示例1——使用自定义注解
    //使用自定义的注解:
    @AnnotationTest(value = "hello")
    // 自定义注解中加了String value属性之后需要对属性进行赋值,当属性有默认值之后可以不赋值
    public class AnnotationUsage
    {
        // 使用自定义的注解:
        @AnnotationTest("world")
        // 不加value=也是可以的
        public void method()
        {
            System.out.println("usage of annotation!");
        }
    
        public static void main(String[] args)
        {
            AnnotationUsage usage = new AnnotationUsage();
            usage.method();
        }
    
    }

      可以使用关键字default给属性设定默认值,如:String value() default "hello";

      程序示例2  

    注解程序示例2
    public @interface AnnotationTest
    {
    
        String[] value1() default "hello";
        // 不加()就出错了
        // 属性名字叫作value时可以不加名字直接赋值
        // 属性名字不叫作value时给属性赋值必须显式指定名字
        // 可以通过default关键字设置默认值
        
        EnumTest value2();
    }
    
    enum EnumTest
    {
        Hello, World, Welcome;
    }
    //使用自定义的注解:
    @AnnotationTest(value2 = EnumTest.Hello)
    // 自定义注解中加了属性之后需要对属性进行赋值,当属性有默认值之后可以不赋值
    public class AnnotationUsage
    {
        // 使用自定义的注解:
        @AnnotationTest(value1={"Hello","world"}, value2 = EnumTest.World)
        //value1现在是数组,所以可以赋多个值
        public void method()
        {
            System.out.println("usage of annotation!");
        }
    
        public static void main(String[] args)
        {
            AnnotationUsage usage = new AnnotationUsage();
            usage.method();
        }
    
    }

      使用@interface自行定义注解时,实际上是自动继承了java.lang.annotation.Annotation接口,由编译程序自动为您完成其他产生的细节。

      在定义Annotation型态时,不能继承其他的Annotation型态或是接口。

      要想定义注解只能通过@interface关键字来定义,手动继承Annotation接口并不能定义一个注解类型,即如果定义一个接口继承了Annotation,那么该接口仍然只是一个接口而不是注解。注意Annotation接口本身并不是注解。

      定义Annotation型态时也可以使用包来管理类别,方式类同于类的导入功能。

    告知编译程序如何处理@Retention

      java.lang.annotation.Retention型态可以在您定义Annotation型态时,指示编译程序该如何对待您自定义的Annotation型态。

      预设上编译程序会将Annotation信息留在.class档案中,但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息。

      在使用Retention型态时,需要提供

      java.lang.annotation.RetentionPolicy的枚举型态:

      

    package java.lang.annotation;
    
    public enum RetentionPolicy
    {
         SOURCE,   //编译程序处理完Annotation信息后就完成任务,注解只会存在于源文件当中,编译器将其丢弃,不会把注解编译到class文件当中,
    
          CLASS,      //编译程序将Annotation存储于class档中,缺省,即默认情况是这种行为
    
          RUNTIME   //编译程序将Annotation存储于class档中,可由VM读入,可以通过反射的方式读取到
    
    }

      RetentionPolicy为SOURCE的例子是@SuppressWarnings。

      仅在编译时期告知编译程序来抑制警告,所以不必将这个信息存储于.class档案。

      RetentionPolicy为RUNTIME的时机,可以像是您使用Java设计一个程序代码分析工具,您必须让VM能读出Annotation信息,以便在分析程序时使用。搭配反射(Reflection)机制,就可以达到这个目的。

      java.lang.reflect.AnnotatedElement接口。其中包含了这样几个方法:

        <T extends Annotation> T getAnnotation(Class<T> annotationClass)

        Annotation[] getAnnotations()

        Annotation[] getDeclaredAnnotations()

        boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

      Class, Constructor, Method, Field, Package等类别,都实现了AnnotatedElement接口

      程序示例3——通过反射获取注解信息

      首先自定义注解类型: 

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation
    {
        String hello() default "shengqishi";
        String world();
    
    }

      之后在类中使用自定义的注解:

    @MyAnnotation(hello="beijing",world="tianjin")
    public class MyTest
    {
    
        @MyAnnotation(hello="shanghai",world="guangzhou")
        @Deprecated
        @SuppressWarnings("unchecked")
        public void output()
        {
            System.out.println("Output something.");
        }
    }

      通过反射获取注解信息:  

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    
    //通过反射获取注解信息
    public class MyReflection
    {
    
        public static void main(String[] args) throws Exception
        {
            MyTest myTest = new MyTest();
    
            // 获取Class对象
            Class<MyTest> c = MyTest.class;
    
            // 获取Method对象
            Method method = c.getMethod("output", new Class[] {});
    
            // 判断是否存在指定类型的注解
            if (method.isAnnotationPresent(MyAnnotation.class))
            {
                // 如果存在该类型注解,这执行这个方法
                method.invoke(myTest, new Object[] {});
    
              // 如果MyAnnotation前面是@Retention(RetentionPolicy.RUNTIME),则执行
                // 如果MyAnnotation前面是@Retention(RetentionPolicy.CLASS),或SOURCE,则括号内语句不执行
                // 因为只有为RUNTIME时,注解信息会被读取过来,其他两种情况注解不能被反射读取过来
    
                // 返回注解
                MyAnnotation myAnnotation = method
                        .getAnnotation(MyAnnotation.class);
    
                // 获取并打印注解中的信息
                String hello = myAnnotation.hello();
                String world = myAnnotation.world();
                System.out.println(hello + ", " + world);
            }
    
            // 获取方法的全部的Annotation
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations)
            {
                System.out.println(annotation.annotationType().getName());
            }
            // 运行结果为:
            // com.learnjava.annotation.MyAnnotation
            // java.lang.Deprecated
            // 因为只能获取到RetentionPolicy为RUNTIME的注解
    
        }
    }

    限定annotation使用对象@Target

      使用java.lang.annotation.Target可以定义其使用之时机。即表示注解可以修饰什么(修饰类、方法、注解等)。

      在定义时要指定java.lang.annotation.ElementType的枚举值之一。

      

    package java.lang.annotation;
    
    public enum ElementType
    {
          TYPE,   //适用class,interface,enum
    
          FIELD, //适用field
    
          METHOD,   //适用method
    
          PARAMETER,   //适用method上之parameter
    
          CONSTRUCTOR,    //适用constructor
    
          LOCAL_VARIABLE,     //适用局部变量
    
          ANNOTATION_TYPE,   //适用annotation型态
    
          PACKAGE,   //使用package
    }

      程序示例4——设定注解目标  

    注解Target设定示例
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Target;
    
    
    @Target(ElementType.METHOD)
    public @interface MyTarget
    {
        String value();
    
    }
    
    public class MyTargetTest
    {
        @MyTarget("hello")
        // 此注解只能被用于修饰方法,放在别的地方会出错
        public void doSomething()
        {
            System.out.println("Do something!");
        }
    
    }

      

    要求为API文件@Documented

      想要在使用者制作JavaDoc文件的同时,也一并将Annotation的讯息加入API文件中,使用

      java.lang.annotation.Documented

      程序示例5——注解加入文档 

    加入文档的注解
    import java.lang.annotation.Documented;
    
    @Documented
    // 表示注解DocumentedAnnotation将会生成到文档里面去
    public @interface DocumentedAnnotation
    {
        String hello();
    
    }
    
    
    
    public class DocumentedTest
    {
        @DocumentedAnnotation(hello="welcome")
        public void method()
        {
            System.out.println("Hello World!");        
        }
    
    }

      

    子类是否继承父类@Inherited

      缺省状态下,父类中的annotation并不会被子类继承。

      可以在定义Annotation型态时加上java.lang.annotation.Inherited型态的Annotation。

      比较简单,不再举例。

     

    参考资料

      张龙老师Java SE视频教程。

      另:官方文档是最好的参考资料。

  • 相关阅读:
    Asp.Net MVC4开发二: Entity Framework在Asp.Net MVC4中的应用
    敌兵布阵(杭电1166)(树状数组)
    alibaba dexposed初步解析
    shell学习三十二天----read读取一行
    cocos2d-x CCScrollView 源代码分析
    语言-编程语言:Python
    GitHub:Python
    GitHub-Microsoft:DotNet4
    GitHub-Microsoft:DotNet3
    GitHub-Microsoft:DotNet2
  • 原文地址:https://www.cnblogs.com/mengdd/p/2890182.html
Copyright © 2011-2022 走看看