1       注解的概念

Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始引入的概念。

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:

- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息

- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。

- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

 

我们可以把注解看成一张标签,贴在类或者方法上的标签。


2       注解的语法

1,注解通过 @interface 关键字进行定义。

   2,同class 和interface 一样,注解也属于一种类型。

    public @interface TestAnnotation {

    }

 3,使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。

 

3    注解的应用

创建一个类 Test,然后在类定义的地方加上 @TestAnnotation 就可以用 TestAnnotation 注解这个类了。

            @TestAnnotation

            public class Test {

              

            }

 

4    元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。

元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

            注解                  

说明

@Target

表示该注解的作用域,说明它可以用在什么地方,由ElementType枚举定义

CONSTRUCTOR:可以给构造方法进行注解

FIELD:可以给属性进行注解(包括enum实例)

LOCAL_VARIABLE:可以给局部变量进行注解

METHOD:可以给方法进行注解

PACKAGE:可以给一个包进行注解

PARAMETER:可以给一个方法的参数进行注解

TYPE:可以给一个类型进行注解,比如类、接口(包括注解类型)、枚举

ANNOTATION_TYPE:可以给一个注解进行注解

TYPE_PARAMETER:类型参数声明(1.8新加入)

TYPE_USE:类型使用声明(1.8新加入)

PS:当注解未指定Target值时,此注解可以使用任何元素之上,就是上面的类型

@Retention

表示该注解的生命周期,说明了这个注解的的存活时间,由RetentionPolicy枚举定义

SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

CLASS:注解在class文件中可用,但会被JVM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机(JVM)中)

RUNTIME:JVM将在运行期也保留注解信息,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息)

PS:当注解未定义Retention值时,默认值是CLASS

@Retention 相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。

@Documented

它的作用是能够将注解中的元素包含到 Javadoc 中去。

@Inherited

允许子类继承父类的注解。

它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。 

默认情况下注解并不会被继承到子类中,可以在自定义注解时加上java.lang.annotation.Inherited注解声明使用继承。

注解的继承只能作用在类上,方法上的注解不会被继承,Interface中的所有注解不会被继承。

@Repeatable

 

JDK1.8新加入的,是可重复的意思。可以在同一个位置重复相同的注解,通常是注解的值可以同时取多个。

5   注解的属性

1,注解的属性也叫做成员变量。,

2,注解只有成员变量,没有方法。

3,注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

4,成员的类型是受限的,合法的类型包括8 种基本数据类型(char,boolean,byte,short,int,long,float,double)外加 String、Class、Annotation、Enumeration及它们的数组。

5,如果只有一个成员,则成员名可以取名为value(),在使用时可以忽略成员名和赋值号(=)。

6,注解类可以没有成员,没有成员的注解成为标志注解。

7,注解中属性可以有默认值,默认值需要用 default 关键值指定。

            @Retention(RetentionPolicy.RUNTIME)

            @Target(value = { ElementType.FIELD,ElementType.TYPE })

            public @interface TestAnnotation {

               int id() default 5;

               String name() default "no name";

     }

上面代码定义了 TestAnnotation 这个注解中拥有 id 和 name 两个属性。在使用的时候,我们应该给它们进行赋值。

赋值的方式是在注解的括号内以 value=”” 形式,多个属性之间用 ,隔开。

        @TestAnnotation(id=5,name="my name")

        public class Test {

        }

数组变量:

        public @interface MyAnnotation {

         String[] value() default "abc";

        }

        @MyAnnotation({ "a", "b", "c" })

        public class Test {

       }


 6    Java 预置的注解

@Deprecated用来标记过时的元素

@Override子类要覆写父类中的方法

@SuppressWarnings阻止警告的意思。

@SafeVarargs参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 的版本中加入的。

@FunctionalInterface函数式接口注解,这个是 Java 1.8 版本引入的新特性。函数式编程很火,所以 Java 8 也及时添加了这个特性。

JDK自带的注解都是编译时注解。


 7   注解的提取

注解通过反射获取。

通过反射获取类、函数或成员上运行时注解信息,从而实现动态控制程序运行的逻辑。

首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解:

public boolean isAnnotationPresent(Classextends Annotation> annotationClass)

 

然后通过 getAnnotation() 方法来获取 Annotation 对象。返回指定类型的注解:

public extends Annotation> A getAnnotation(Class annotationClass)


或者是 getAnnotations() 方法。返回注解到这个元素上的所有注解

 public Annotation[] getAnnotations()

 

示例:

            @TestAnnotation(id = 1, name = "MyClass Name")

            public class MyClass {

             

               @TestAnnotation(id = 5, name = "field name")

               String name;

             

               public String getName() {

                  return "name";

               }

             

               public static void main(String[] args) {

                  boolean hasAnnotation = MyClass.class.isAnnotationPresent(TestAnnotation.class);

                  if (hasAnnotation) {

                     TestAnnotation testAnnotation = MyClass.class.getAnnotation(TestAnnotation.class);

                     System.out.println("id:" + testAnnotation.id());

                     System.out.println("name:" + testAnnotation.name());

                  }

             

                  try {

                     Field nameField = MyClass.class.getDeclaredField("name");

                     nameField.setAccessible(true);

                     TestAnnotation ta = nameField.getAnnotation(TestAnnotation.class);

                     System.out.println(ta.id());

                     System.out.println(ta.name());

             

                  } catch (NoSuchFieldException e) {

                     e.printStackTrace();

                  } catch (SecurityException e) {

                     e.printStackTrace();

                  }

             

               }

    }

             

提取注解的注解:

        @Retention(RetentionPolicy.RUNTIME)

        @Target(value = { ElementType.FIELD,ElementType.TYPE })

        public @interface SuperAnnotation {

           int size();

        }

 

        @SuperAnnotation(size = 7)

        @Retention(RetentionPolicy.RUNTIME)

        @Target(value = { ElementType.FIELD, ElementType.TYPE })

        public @interface TestAnnotation {

          

           int id() default 5;

           String name() default "no name";

        }

 

        @TestAnnotation(id = 1, name = "MyClass Name")

        public class MyClass {

         

           @TestAnnotation(id = 5, name = "field name")

           String name;

         

           public String getName() {

              return "name";

            }

 

        public static void main(String[] args) {

          boolean hasAnnotation = MyClass.class.isAnnotationPresent(TestAnnotation.class);

          if (hasAnnotation) {

             TestAnnotation testAnnotation = MyClass.class.getAnnotation(TestAnnotation.class);

              Annotation[] ts = testAnnotation.annotationType().getAnnotations();

             System.out.println(ts);

          }

      }

    }

 

8   注解的使用场景

注解有什么用?给谁用?给 编译器或者 APT 用的。

注解无法改变代码本身,注解只是某些工具的的工具。注解主要针对的是编译器和其它工具软件(SoftWare tool)。当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。

 

 


秒懂,Java 注解 (Annotation)你可以这样学

Java注解全面解析

java注解的自定义和使用

框架基础——全面解析Java注解

框架开发之Java注解的妙用