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视频教程。

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

  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/mengdd/p/2890182.html
Copyright © 2011-2022 走看看