注解(Annotation),以标签的形式存在于代码中,是Java代码里的特殊标记,它为Java 程序代码提供了一种形式化的方法,用来表达额外的某些信息,这些信息代码本身是无法表示的。可以方便的使用注解修饰程序元素,这里的程序元素包括类、方法、成员变量等。
注解的存在不会影响程序代码的编译和执行,只是用来生成其他文件或使我们在运行代码时知道被运行代码的描述信息。
一、使用方式:
在程序元素钱加上"@" 符号,就像一个修饰符一样,用于修饰它所支持的程序元素。
//格式 @Annotation(参数) //其中: 1、Annotation:表示注解类型 2、注解参数可以没有也可以有一个或多个
使用规则规范:
1、将注解置于所有修饰符之前
2、通常将注解单独放置在一行
3、默认情况下,注解可以用于修饰任何程序元素,包括类、方法和成员变量等。
二、分类
主要分为三类:内建注解、元注解和自定义注解
1、内建注解
常见的三种标准的注解类型 @Override、 @Deprecated 、@SuppressWarnings() 位于java.lang 包中
1.1、@Override 注解
@Override 注解用来标注方法,表示该方法是重写父类的某方法。一般都会自动在重写的子类上自动添加
1.2、 @Deprecated 注解
@Deprecated注解用于标识过时的程序元素。
如果一个程序元素被 @Deprecated 注解修饰,则表示程序元素已过时,编译器将不再鼓励使用这个被标注的程序元素。
1.3、@Suppress Warnings 注解
@Suppress Warning 注解用于有选择的关闭编译器,对类、方法和成员变量等程序元素的警告。会一直作用于该程序元素的所有子元素。
//@SuppressWarning常用的注解参数 deprecation:使用了过时的程序元素 unchecked : 执行了未检查的转换 unused : 有程序元素未被使用 fallthrough : switch程序块直接通往下一种情况而没有break path : 在类路径、源文件路径等中有不存在的路径 serial : 在序列化的类上缺少serialVersionUID 定义 finally : 任finally 子句不能正常完成 all : 所有情况
使用方式如下:
2、元注解
java.lang.annotation 包下提供了4个元注解,它们用来修饰其他的注解定义。(用于注解的定义中)
分别是:@Target 注解,@Retention 注解,@Documneted 注解,@Inherited 注解
2.1、@Target 注解
@Target 注解用于指定被修饰的注解,所作用的范围
可用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
@Target注解用于指定被其修饰的注解能用于修饰那些程序元素,@Target注解类型有唯一的value作为成员变量。这个成员变量是java.lang.annotation.ElementType 类型。ElementType类型是可以被标注的程序元素的枚举类型。@Target的成员变量value 为如下值是,则可指定被修饰的注解只能按如下声明进行标注,当value为FIELD时,被修饰的注解只能用来修饰成员变量。
ElementType.ANNOTATION_TYPE : 注解声明
ElementType.CONSTRUCTOR : 构造方法声明
ElementType.FIELD : 成员变量声明
ElementType.LOCAL_VARIABLE :局部变量声明
ElementType.METHOD : 方法声明
ElementType.PACKAGE : 包声明
ElementType.PARAMETER : 参数声明
ElementType.Type : 类、接口(包括注解类型)或枚举声明
2.2、@Retention 注解
@Retention 注解用于指定被修饰的注解,保留时间的长短,即被修饰注解的生命周期
如:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(但并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用meta-Annotation可以对 Annotation的“生命周期”限制。
@Retention注解描述了被其修饰的注解是否被编译其丢弃或保留在class 文件中,默认情况下,注解被保留在class文件中,但在运行时并不能被反射访问。
@Retention 包含一个Retention Policy类型的value 成员变量,其取值来自java.lang.annotation.RetentionPolicy的枚举类型值由如下3个
RetentionPolicy.CLASS : @Retention 注解中value成员变量的默认值,表示编译器会把被修饰的注解记录在class 文件中,但当运行java 程序时,java 虚拟机不再保留注解,从而无法通过反射对注解进行访问。
RetentionPolicy.RUNTIME : 表示编译器将注解记录在class 文件中,当运行java 程序时,java 虚拟机会保留注解,程序可以通过反射获取该注解。
RetentionPolicy.SOURCE : 表示编译器直接丢弃被修饰的注解。
//如下,通过将value 成员变量的值设为RetentionPolicy.RUNTIME,指定@Retention注解在运行时可以通过反射进行访问 @Retetion(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retetion { RetentionPolicy.value(); }
2.3、@Documented 注解
@Documented 用于指定被其修饰的注解被JavaDoc 工具提取成文档,如果在定义某注解时使用了@Document修饰,则所有使用该注解修饰的程序元素的的API文档中都将包含该注解说明。而且@Document注解类型没有成员变量,一个标记注解
2.4、@Inherited 注解
@Inherited 注解可以看成一个标记注解,被修饰的是被继承的
如果一个使用了@Inherited注解修饰的注解被用于某个类,则这个注解也将被用于该类的子类。
当@Inherited 注解类型标注的注解的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited 注解类型的注解时,反射代码检查将展开工作:检查class和其父类,直到发现指定的注解类型被发现,或者到达类继承结构的顶层.
3、自定义注解
注解是一种接口,但又不同于接口,借助@interface 关键字定义新的注解。
// 访问修饰符 注解关键字 注解名 {} public @interface InAnnstu {}
注解类型定义后,就可以使用他来修饰程序中的类、接口、方法和成员变量等程序元素,不过不怎么使用。
三、读取注解信息
借助java.lang.reflect 包中一些使用反射功能的工具类,reflect 包中也提供了对读取运行时注解的支持。
其中java.lang.reflect 包下的AnnotatedElement接口是所有程序元素的父接口,其实现方式主要有
Class : 类定义
Constructor :构造方法定义
Field :类的成员变量定义
Method : 类的方法定义
Package :类的包定义
程序通过反射获得某个类的AnnotatedElement对象(如类、方法和成员变量),调用该对象的三个方法来访问注解信息
getAnnotation() 方法 //用于返回该程序元素上存在的、指定类型的注解,如果该类型的注解不存在,则返回null getAnnotations() 方法 //用来返回该程序元素上存在的所有注解 getAnnotationPresent() 方法 //用来判断该程序元素上是否包含指定类型的注解,如果存在则返回true,否则返回false
需要预先设设置,目的是为了获取 MyAnnotationStu类的 getObjectInfo()方法的所有注解
package com.obge.annstu; import java.lang.annotation.Annotation; public class MyAnnotationStu { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //获取 MyAnnotationStu 类的 getObjectInfo() 方法的所有注解,并输出 Annotation[] arrann = Class.forName("com.obge.annstu.MyAnnotationStu").getMethod("getObjectInfo").getAnnotations(); //遍历所有注解 for(Annotation anno:arrann){ System.out.println(anno); } } }
注意:上述要想可以得到反馈,需要注解被定义为运行时的注解,就是使用@Retention(RetentionPolicy.RUNTIME)修饰的注解。因为一个注解类型被定义为运行时注解时,才可在运行时可见,当Class 文件被装载时,保存在class 文件的中的注解才会被 JVM 所读取。
当想获得某个注解里的元数据时,可以采用将注解强制转换为所需的注解类型,然后通过注解对象的抽象方法来访问这些元数据。