概述
注解是提高Java开发效率的重要工具。
通过注解,很多配置文件和逻辑可以用注解来代替,使代码更加简洁,清晰。因此,大部分的开源框架都大量使用了注解,例如Spring中,注解配合反射实现的控制反转是其核心理念之一。注解的内容涵盖如图的内容:
我将挑选其中必须的部分讲述。
注解介绍:
如图是java.lang.annotation包的结构:
我们发现里面包含一个Annotation接口,五个以“@”开头的注释,三个相关的Error和Exception,以及两个Enum类型。
下面是一个我写的自定义注释:
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface Description{
String value();
}
注解定义
注解的标志是@interface
,编译器会使它自动继承java.lang.annotation.Annotation接口,做一系列的工作将它编译成一个注解。在定义注解时,不能继承其他的注解或接口。
元注解
注解前面的@Target,@Retention等是注解的注解,称为元注解。
共有:@Target,@Retention,@Inherited,@Documented,@Native五个注解。
@Target:
用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值使用了枚举类型(ElementType),有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention
用于描述注解的保留时间,
取值也是枚举类型(RetentionPolicy),有:
1.SOURCE,在原文件中有效
2.CLASS,在class文件中有效
3.RUNTIME,运行期间有效
@Inherited
用于描述注释可以被继承,即如果这个注解应用于一个父类,它的子类也将拥有这个注解。
@Docemented
用于描述注解应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Native
表示定义了一个可能被本地方法使用的常量,这个注解被一些工具用来识别已加载必要的头文件。
注解体
注解体中只能定义一系列的属性,不能定义方法。被注解的对象类似于继承了这些属性。
注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
注解参数的默认值:
注解参数可以设定默认值,如
@interface Palette{
public enum Color {RED,YELLOW,BLUE,WHITE};
String color() default Color.WHITE;
String name default "";
}
默认属性value
任何一个注解都有一个默认属性value(String类型)。例如,当在一个类上加了@MyAnnotation(“hello”)的注释,这个类就类似于继承了MyAnnotation的属性,其中必然有的属性是value,值为“hello”。
JDK中的注解:
@Override 重载
@Deprecated 过时
@SuppressWarnnings 抑制编译器警告
SuppressWarnings注解的常见参数值的简单说明:
1.deprecation:使用了不赞成使用的类或方法时的警告;
2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
3.fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
4.path:在类路径、源文件路径等中有不存在的路径时的警告;
5.serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
6.finally:任何 finally 子句不能正常完成时的警告;
7.all:关于以上所有情况的警告。
注解使用
AnnotatedElement
注解必须被使用才有意义,使用的方法是通过反射。核心的接口是java。lang.reflect.AnnotatedElement。接口的outline如下:
isAnnotationPresent()
判断类是否有注解,返回boolean类型。
getAnnotation(Class <T>)
寻找特定的注解类型,返回注解对象,没有这个类型的注解,则返回空。
getAnnotations()
寻找对象所包含的注解类型数组,如果有重复的注解,每个类型只计一个。
getAnnotationsByType()
返回特定注解类型的注解数组。
这里就需要说到重复注解的问题:
如下:
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Repeatable(Descriptions.class)
@interface Description{
String value();
int put() default 1;
}
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface Descriptions{
Description[] value();
}
当一个注解在另一个注解中以数组形式存在,并且被注解为Repeatable,那么这个注解可以在一个对象上多次注解。比如在Web应用里注册多个过滤器,拦截器。
对于重复注解,getAnnotations()只能获得一次,但getAnnotationsByType()可以就获得多次。
而下面的
getDeclaredAnnotation();
getDeclaredAnnotations();
getDeclaredAnnotationsByType();
则是只获取本类的注解,忽略类所继承的类和实现的接口的注解。
通过源码可以发现,反射相关的类如,Field,Constructor,Method都继承Executable类,Executable类继承AccessbaleObject,AccessbaleObject实现了AnnotationElement接口。
所以这些类可以使用以上方法来获取注解,然后以类似类的方法,获取注解中的属性,如下例所示。
package com.way.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class LearnAnnotation {
public static void main(String[] args) {
Class c=null;
try {
c=Class.forName("com.way.annotation.F");
if(c.isAnnotationPresent(Description.class)){
Description d=(Description)c.getAnnotation(Description.class);
System.out.println(d.value());
Method[] ms=c.getMethods();
for(Method m:ms){
if(m.isAnnotationPresent(Description.class)){
Description dd=m.getAnnotation(Description.class);
System.out.println(dd.value());
}
}
Field[] fs=c.getFields();
for(Field f:fs){
if(f.isAnnotationPresent(Description.class)){
Description ddd=f.getAnnotation(Description.class);
System.out.println(ddd.value());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
一个辅助的类,对该类添加注解
package com.way.annotation;
@Description("I am class description")
class F{
@Description("I am method description")
public String getName(){
return null;
}
@Description("I am field description")
public String name;
@Description("I am field description")
public int id;
public int age;
}
控制台输出,可以看到,可以获取到相应注解的内容
I am class description
I am method description
I am field description
I am field description