注解
其实注解就是注释的意思。我想这样叫法是跟平时用的注释不一样吧。
Java内置了三种注解,想必都见过。
@Override
@Deprecated
@SuppressWarnings
好奇的小伙子,会点击进去一探究竟。发现又一批注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
继续点击又会发现Target、Retention、Documented注解互相出现。这三个加上Inherited称为元注解。即注解的注解。这有点绕,其实就是用来声明注解的东西。那四个都代表什么意思呢。
元注解
Target注解指示注释类型所适用的程序元素的种类。先来看看Target内部,省去上边的注解,它跟接口不同就是interface前面加个@。注解都是这个模样。内部的方法也称为成员。Target里边有ElementType枚举数组成员,分别表示不同的作用领域。
...
public @interface Target {//被标记为Target名的注解
ElementType[] value();//成员
}
public enum ElementType {
TYPE, //类、接口(包括注释类型)或枚举声明
FIELD, //字段声明(包括枚举常量)
METHOD, //方法声明
PARAMETER, //参数声明
CONSTRUCTOR, //构造方法声明
LOCAL_VARIABLE, //局部变量声明
ANNOTATION_TYPE, //注释类型声明
PACKAGE //包声明
}
Retention注解指示注释类型的注释要保留多久。内部的成员是RetentionPolicy枚举。分别表示不同生命周期。比如Override、Deprecated、SuppressWarnings注解都属于SOURCE类型,用完就丢。RUNTIME类型将会保存注解,因此可以反射性地读取,也是我们自定义注解用到的。
...
public @interface Retention {
RetentionPolicy value();
}
public enum RetentionPolicy {
CLASS, //编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。
RUNTIME, //编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
SOURCE//编译器要丢弃的注释。
}
剩下的Documented和Inherited注解都是没有成员。Documented注解指示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化。Inherited注解指示注释类型被自动继承。用的也不多,就不深入了。
public @interface Documented {
}
public @interface Inherited {
}
自定义注解
自定义注解至少使用Target注解和Retention注解。首先创建一个接口,在interface前面加一个@。同时声明Target注解和Retention注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetType {
}
//声明在类名上面
@TargetType
public class Person {
@TargetType //报错,声明地方不对
private String name;
}
声明不同的地方,就需要另外创建注解。
@Target(ElementType.FIELD)//指定字段
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetField {
}
@Target(ElementType.METHOD)//指定方法
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetMethod {
}
//以下使用正确无误
@TargetType
public class Person {
@TargetField
private String name;
@TargetMethod
public void printName(){
System.out.println(name);
}
}
没有成员的注解,相当是一个标识。使其让注解丰富起来,可以加上成员。在接口内部加上方法,可以指定默认值。没有指定默认值,那就是必填,不然会报错。要注意的是成员只支持基本类型、String及枚举类型。
public @interface TargetField {
String name() default "无名";
int age();
}
@TargetField(name= "name",age=18)
private String name;
只有一个成员,直接使用value方法,可以省略xxx=。
public @interface TargetField {
String value();
}
//对应value 不需要写xxx==
@TargetField("name")
private String name;
注解处理
注解是标识了代码,但是没卵用啊。的确是这样,处理它们之前是真的没什么用。注解永远不会改变被注解的代码的语义,但是它可以通过工具进行特殊的处理。Class类下会有一些getDeclaredXXX的方法,表示反映此 Class 对象所表示的类或接口所声明的所有XXX。相对应的Field、Method、Constructor都有getAnnotation方法获取注解类。
Class cls = Person.class;
Field[] fields = cls.getDeclaredFields();//获取字段
for (Field f : fields) {
if (f.isAnnotationPresent(TargetField.class)) {//判断是否存在注解类
TargetField targetField = f.getAnnotation(TargetField.class);//获取注解类
String value = targetField.value();//取出属性 name
}
}
获取被注解的方法也差不多,要是直接调用方法会报错,要先把方法为static修饰才可以。
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(TargetMethod.class)) {
try {
m.invoke(null);
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
}
}
}
总的来说,注解并不难,只是被注解是一种标识,也可以取属性值。掌握注解就很多玩法了,配置处理应用注解比较多。不足之处请指正。