[转帖收集] Java注解

Posted 枕上轻寒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转帖收集] Java注解相关的知识,希望对你有一定的参考价值。

1、Annotation


       它的作用是修饰编程元素。什么是编程元素呢?例如:包、类、构造方法、方法、成员变量等。Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。

      Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在Annotation的“name=value”结构对中。

      Annotation的成员在Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认语法:允许声明任何Annotation成员的默认值:一个Annotation可以将name=value对作为没有定义默认值的Annotation成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也可以被子类覆盖。

  Annotation能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。另外,尽管一些annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了Annotation,导致了annotation类型在代码中是“不起作用”的; 只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理。本文中将涵盖标准的Annotation和meta-annotation类型,陪伴这些annotation类型的工具是java编译器(当然要以某种特殊的方式处理它们)。

2、什么是metadata(元数据):


  元数据从metadata一词译来,就是“关于数据的数据”的意思。
  元数据的功能作用有很多,比如:你可能用过Javadoc的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
    1. 编写文档:通过代码里标识的元数据生成文档
    2. 代码分析:通过代码里标识的元数据对代码进行分析
    3. 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查
  在Java中元数据以标签的形式存在于Java代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或针在运行时知道被运行代码的描述信息。
  综上所述:
    第一,元数据以标签的形式存在于Java代码中。
    第二,元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
    第三,元数据需要编译器之外的工具额外的处理用来生成其它的程序部件。
    第四,元数据可以只存在于Java源代码级别,也可以存在于编译之后的Class文件内部。

3、Annotation和Annotation类型:


  Annotation使用了在java5.0所带来的新语法,它的行为十分类似public、final这样的修饰符。每个Annotation具有一个名字和成员个数>=0。每个Annotation的成员具有被称为name=value对的名字和值(就像javabean一样),name=value装载了Annotation的信息。

  Annotation类型定义了Annotation的名字、类型、成员默认值。一个Annotation类型可以说是一个特殊的java接口,它的成员变量是受限制的,而声明Annotation类型时需要使用新语法。当我们通过java反射api访问Annotation时,返回值将是一个实现了该 annotation类型接口的对象,通过访问这个对象我们能方便的访问到其Annotation成员。后面的章节将提到在java5.0的 java.lang包里包含的3个标准Annotation类型。

4、注解的分类:


  根据注解参数的个数,我们可以将注解分为三类:
    1.标记注解:一个没有成员定义的Annotation类型被称为标记注解。这种Annotation类型仅使用自身的存在与否来为我们提供信息。比如后面的系统注解@Override;
    2.单值注解
    3.完整注解  
  根据注解使用方法和用途,我们可以将Annotation分为三类:
    1.JDK内置系统注解
    2.元注解
    3.自定义注解

5、系统内置标准注解:


  注解的语法比较简单,除了@符号的使用外,他基本与Java固有的语法一致,JavaSE中内置三个标准注解,定义在java.lang中:
    @Override:用于修饰此方法覆盖了父类的方法; ---起到了断言的作用。
    @Deprecated:用于修饰已经过时的方法; ---编译器将不鼓励使用这个被标注的程序元素。
    @SuppressWarnnings:用于通知java编译器禁止特定的编译警告。
-----Deprecated具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。
----SuppressWarning不是一个标记注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。

SuppressWarnings注解的常见参数值的简单说明:
    1.deprecation:使用了不赞成使用的类或方法时的警告;
    2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
    3.fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
    4.path:在类路径、源文件路径等中有不存在的路径时的警告;
    5.serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
    6.finally:任何 finally 子句不能正常完成时的警告;
    7.all:关于以上所有情况的警告。

annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值。
----@SuppressWarnings(value={ "rawtypes", "unchecked" })

6、自定义注解


1)以@interface关键字定义
(2)注解包含成员,成员以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。
(3)成员赋值是通过@Annotation(name=value)的形式。
(4)注解需要标明注解的生命周期,注解的修饰目标等信息,这些信息是通过元注解实现。
上面的语法不容易理解,下面通过例子来说明一下,这个例子就是Target注解的源码,

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.ANNOTATION_TYPE } )
public @interface Target
{
ElementType[] value();
}
源码分析如下:
第一:元注解@Retention,成员value的值为RetentionPolicy.RUNTIME。
第二:元注解@Target,成员value是个数组,用{}形式赋值,值为ElementType.ANNOTATION_TYPE
第三:成员名称为value,类型为ElementType[]
另外,需要注意一下,如果成员名称是value,在赋值过程中可以简写。如果成员类型为数组,但是只赋值一个元素,则也可以简写。如上面的简写形式为:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)

7、注解的生命周期


注解的定义语法中已经说到了:注解需要标明注解的生命周期,这些信息是通过元注解实现。而这个元注解是:
public @interface Retention
{
RetentionPolicy value();
}
Retention注解的值是enum类型的RetentionPolicy。包括如下几种情况:
(1)SOURCE: 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃。Annotations are to be discarded by the compiler.
(2)CLASS: 注解被保留到class文件,jvm加载class文件时候被遗弃。这是默认的生命周期。
Annotations are to be recorded in the class file by the compiler,
but need not be retained by the VM at run time. This is the default behavior.
(3)RUNTIME: 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,保存到class对象中,可以通过反射来获取。
Annotations are to be recorded in the class file by the compiler and
retained by the VM at run time, so they may be read reflectively.

8、注解的修饰目标


注解的定义语法中已经说到了:注解需要标明注解的修饰目标,这些信息是通过元注解实现。而这个元注解是:
public @interface Target
{
ElementType[] value();
}
这个注解的值是enum类型ElementType。包括以下几种情况:
(1)TYPE:指的是在类,接口(包括注解)或者enum上使用的注解。
(2)FIELD:指的在field属性,也包括enum常量使用的注解。
(3)METHOD:指的是在方法声明上使用的注解。
(4)PARAMETER:指的是在参数上使用的注解,
(5)CONSTRUCTOR: 指的是在构造器使用的注解。
(6)LOCAL_VARIABLE:指的是在局部变量上使用的注解。
(7)ANNOTATION_TYPE:指的是在注解上使用的元注解
(8)PACKAGE:指的是在包上使用的注解。

9、注解的底层实现


定义一个注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Cache
{
String value() default "cache";
}
用javap -verbose 分析其字节码,如下图所示:
                                 
分析上面的字节码,我们可以得出:
第一:public interface Cache extends java.lang.annotation.Annotation,说明Cache注解是继承自Annotation,仍然是interface。
第二:public abstract java.lang.String value(),说明value方法是abstract类型。

10、注解的继承


@Inherited 用于描述Annotation可以被继承
如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。不从接口继承annotation,方法并不从重载的方法继承annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface AnnonitionTargetTest {
}

11、注解处理器类

接口 java.lang.reflect.AnnotatedElement,它的实现类:

java.lang.Class类,
java.lang.reflect.Filed类,
java.lang.reflect.Constructor类,
java.lang.reflect.Method类,
java.lang.Package类
相关方法:
<T extends Annotation> T getAnnotation(Class<T> annotationClass);   返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
Annotation[] getAnnotations();    返回该程序元素上存在的所有注解。
boolean isAnnotationPresent(Class<T extends Annotation> annotationClass);    判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
Annotation[] getDeclaredAnnotations();    返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ps: 内容来自以下博文,侵删。

 http://www.cnblogs.com/peida/archive/2013/04/23/3036035.html

http://swiftlet.net/archives/1906

http://www.cnblogs.com/imeng/p/4023125.html

 

以上是关于[转帖收集] Java注解的主要内容,如果未能解决你的问题,请参考以下文章

Java反射注解妙用(获取所有接口说明)

Java项目构建基础之统一异常

java元注解,局部注解怎么解析

java注释的java注解

ButterKnife的原理简述

java注解的参数没有被赋值