Java注解,从名字上看是注释,解释。但功能却不仅仅是注释那么简单。
注解(Annotation)即元数据,就是源代码的元数据
注解(Annotation)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻方便地使用这些数据(通过解析注解来使用这些数据).
注解(Annotation)是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
1.作用
* 生成文档
* 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
* 在编译时进行格式检查,如@Override等
* 每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。
包java.lang.annotation中包含所有定义自定义注解所需用到的原注解和接口。如接口java.lang.annotation.Annotation是所有注解继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object。
该包同时定义了四个元注解,Documented,Inherited,Target(作用范围,方法,属性,构造方法等),Retention(生命范围,源代码,class,runtime)。
(元注解:自定义注解的时候用到的,也就是自定义注解的注解,也就是自定义注解的注释,解释)
@Target
@Target说明了所修饰注解的范围:该注解可被用于 packages、types(类、接口、枚举、Annotation类型)、
类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。
在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
类型 | 用途 |
CONSTRUCTOR | 用于描述构造器 |
FIELD | 用于描述域 |
LOCAL_VARIABLE | 用于描述局部变量 |
METHOD | 用于描述方法 |
PACKAGE | 用于描述包 |
PARAMETER | 用于描述参数 |
TYPE | 用于描述类、接口 或enum声明 |
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;
而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,
而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。
使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
类型 | 用途 | 说明 |
SOURCE | 在源文件中有效 | 仅出现在源代码中,而被编译器丢弃 |
CLASS | 在源文件中有效 | 被编译在class文件中 |
RUNTIME | 在运行时有效 | 编译在class文件中 |
@Documented
作用:将注解包含在javadoc中,它代表着此注解会被javadoc工具提取成文档。
@Inherited
自定义注解
格式:
public @interface 注解名{
定义体
}
注解参数的可支持数据类型:
所有基本数据类型(int,float,double,boolean,byte,char,long,short)
String 类型
Class类型
enum类型
Annotation类型
以上所有类型的数组
规则:
* 修饰符只能是public 或默认(default)
* 参数成员只能用基本类型byte,short,int,long,float,double,boolean八种基本类型和
* String,Enum,Class,annotations及这些类型的数组
* 如果只有一个参数成员,最好将名称设为”value”
* 注解元素必须有确定的值,可以在注解中定义默认值,也可以使用注解时指定,非基本类型的值不可为null,常* 使用空字符串或0作默认值
* 在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值
最简单的例子:
public @interface TestA {
//这里定义了一个空的注解,它能干什么呢。我也不知道,但它能用。
}
在下面这个程序使用它:
@TestA //使用了类注解
public class UserAnnotation {
@TestA //使用了类成员注解
private Integer age;
@TestA //使用了构造方法注解
public UserAnnotation(){
}
@TestA //使用了类方法注解
public void a(){
@TestA //使用了局部变量注解
Map m = new HashMap(0);
}
public void b(@TestA Integer a){ //使用了方法参数注解
}
}
这个注解也太简单了吧,好像什么信息也不能传递。别急下面就来一步步完善它,也该四位元注解依次开始上场了。
@Target(ElementType.TYPE)
public @interface TestA {
}
测试类那边立马出现了一堆错误,除了类注解。我想到这,聪明的你立刻明白了这个元注解的意义了。是不是想当然的偷起懒来了?难道还有意外?细心的朋友应该发现了,我们的测试类少了一个属性没用,就是ElemenetType.PACKAGE。在我们的注解加上这个属性的元注解后,我们测试程序的元注解全部阵亡,不对,还有一个没加呢,好加上。package 包,想当然是加载 package 前面。即
@TestA package com.king.annotation;
什么,也报错?这就搞不明白了,不加在这加哪去呢。我也不知道了,不过这是编译错误,我们的IDE将错误给我们指出了,就是
Package annotations must be in file package-info.java ,E文虽然不好,但这个简单的还是难不倒的,package注解必须定义在package-info.java中。package-info 又是什么东西,百度..。
OK,到此,@Target元注解就全部完成了。
简单演示下如何使用:
@Target(ElementType.PACKAGE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
}
第三和第四个元注解就不再举例了。
下面我们还是继续来深入的探讨下注解的使用。上面的例子都非常简单,注解连属性都没有。
OK,下面我们就来定义一个有属性的注解,并在例子程序中获取到注解中定义的值。
开始之前讲下定义属性的规则:
@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。
方法的名称就是参数的名称,返回值类型就是参数的类型(参考上面)。
可以通过default来声明参数的默认值。
代码:
/*
* 自定义注解 TestA
* 为方便测试:注解目标为类方法,属性及构造方法
* 注解中含有三个元素 id ,name和 gid;
* id元素 有默认值 0
*/
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<Long> gid();
}
下面改下我们的测试类:
@TestA(name="type",gid=Long.class) //类成员注解
public class UserAnnotation {
@TestA(name="param",id=1,gid=Long.class) //类成员注解
private Integer age;
@TestA (name="construct",id=2,gid=Long.class)//构造方法注解
public UserAnnotation(){
}
@TestA(name="public method",id=3,gid=Long.class) //类方法注解
public void a(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="protected method",id=4,gid=Long.class) //类方法注解
protected void b(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="private method",id=5,gid=Long.class) //类方法注解
private void c(){
Map<String,String> m = new HashMap<String,String>(0);
}
public void b(Integer a){
}
}
JAVA既然增加了注解,肯定就增加了相关读取的API。
在java.lang.reflect包中新增了AnnotatedElement接口,JDK源码如下:
public interface AnnotatedElement {
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
Annotation[] getDeclaredAnnotations();
}
isAnnotationPresent:判断是否标注了指定注解
getAnnotation:获取指定注解,没有则返回null
getAnnotations:获取所有注解,包括继承自基类的,没有则返回长度为0的数组
getDeclaredAnnotations:获取自身显式标明的所有注解,没有则返回长度为0的数组
public class ParseAnnotation {
/**
* 简单打印出UserAnnotation 类中所使用到的类注解
* 该方法只打印了 Type 类型的注解
* @throws ClassNotFoundException
*/
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class clazz = Class.forName("com.tmser.annotation.UserAnnotation");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
TestA testA = (TestA)annotation;
System.out.println("id= \""+testA.id()+"\"; name= \""+testA.name()+"\"; gid = "+testA.gid());
}
}
/**
* 简单打印出UserAnnotation 类中所使用到的方法注解
* 该方法只打印了 Method 类型的注解
* @throws ClassNotFoundException
*/
public static void parseMethodAnnotation(){
Method[] methods = UserAnnotation.class.getDeclaredMethods();
for (Method method : methods) {
/*
* 判断方法中是否有指定注解类型的注解
*/
boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
/*
* 根据注解类型返回方法的指定类型注解
*/
TestA annotation = method.getAnnotation(TestA.class);
System.out.println("method = " + method.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
/**
* 简单打印出UserAnnotation 类中所使用到的方法注解
* 该方法只打印了 Method 类型的注解
* @throws ClassNotFoundException
*/
public static void parseConstructAnnotation(){
Constructor[] constructors = UserAnnotation.class.getConstructors();
for (Constructor constructor : constructors) {
/*
* 判断构造方法中是否有指定注解类型的注解
*/
boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
/*
* 根据注解类型返回方法的指定类型注解
*/
TestA annotation =(TestA) constructor.getAnnotation(TestA.class);
System.out.println("constructor = " + constructor.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
parseMethodAnnotation();
parseConstructAnnotation();
}
}