注解的分类
Java注解按照运行机制来分有以下几类
- 源码注解 只在源码中出现,编译成
class
文件就不存在了 - 编译时注解 注解在源码和编译中都存在 例如:
Override
,Deprected
,SuppressWarnings
- 运行时注解 运行阶段起作用,甚至会影响运行逻辑的注解
除了JDK自带的常见注解外,java中提供了四种元注解: @Retention
@Target
@Document
@Inherited
;元注解负责注解其他注解,可以用来自定义注解。
注解名称 | 含义 | 取值 |
---|---|---|
@Target |
注解的目标范围 | @Target(ElementType.TYPE) // 类、接口(包括注解类)或 enum@Target(ElementType.FIELD) // 成员字段、enum常量@Target(ElementType.METHOD) // 成员方法@Target(ElementType.PARAMETER) // 方法参数@Target(ElementType.CONSTRUCTOR) // 构造函数@Target(ElementType.LOCAL_VARIABLE) // 局部变量@Target(ElementType.ANNOTATION_TYPE) // 注解@Target(ElementType.PACKAGE) // 包@Target(ElementType.TYPE_PARAMETER) // 类型参数@Target(ElementType.TYPE_USE) // 类型使用声明 |
@Retention |
注解保留位置 | @Retention(RetentionPolicy.SOURCE) // 注解只在源码中保留,在编译后的class中被抛弃@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解在class文件中可用,但会被VM丢弃,运行时无法获得@Retention(RetentionPolicy.RUNTIME) // 注解在运行期间保留,可以通过反射获取到 |
@Document |
注解将包含在Javadoc中 | |
@Inherited |
允许子类继承父类中的注解 |
简单实例
- Field 注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Input {
String YY_MM_DD = "yyyy-MM-dd";
String YY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
String show() default "";
String dateFormat() default YY_MM_DD_HH_MM_SS;
}
这里指定@Retention(RetentionPolicy.RUNTIME)
即表示在运行时保留注解并且可以通过反射获取到该注解。
新建一个实体类来注解字段:
public class User {
@Input(show = "姓名")
private String name;
@Input(show = "出生日期", dateFormat = Input.YY_MM_DD)
private String birthDay;
@Input(show = "年龄")
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getBirthDay() {
return birthDay;
}
public void setBirthDay(String birthDay) {
this.birthDay = birthDay;
}
}
测试代码:
public class Test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("小明");
user.setBirthDay("1995-4-4");
handleRetention(user);
}
private static void handleRetention(Object object) throws IllegalAccessException {
Class<?> clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Input input = field.getAnnotation(Input.class);
if (input != null) {
loge(TAG, "input.show(): " + input.show() );
field.setAccessible(true);
Object fieldObj = field.get(object);
if (fieldObj != null) {
String s = String.valueOf(fieldObj);
loge(TAG, field.getName() + ": " + s);
}
}
}
}
}
通过返回拿到对象成员的注解类,并获取到注解的值和成员变量的值,从而可以后续执行一些自己的操作(根据需求)。
- Method 注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
String value();
}
这里新建了一个注解类,也可以在同一个接口上同时添加多个target
如@Target({ElementType.METHOD, ElementType.FIELD})
测试类:
public class Test2 {
@Permission("OK")
private void deleteUser() {
// do something
System.out.println("deleteUser");
}
@Permission("FAIL")
private void onError(String s) {
System.out.println(s);
}
public static void main(String[] args) {
Test2 test = new Test2();
try {
Method[] methods = Test2.class.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Permission.class)) {
Permission permission = method.getAnnotation(Permission.class);
if (permission != null) {
if (permission.value().equals("OK")) {
method.invoke(test);
} else if (permission.value().equals("FAIL")) {
method.invoke(test, "没有操作权限!");
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- TYPE 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAnnotation {
}
@ClassAnnotation
public class Test3 {
public static void main(String[] args) {
Annotation[] declaredAnnotations = Test3.class.getDeclaredAnnotations();
if (declaredAnnotations != null) {
for (Annotation annotation : declaredAnnotations) {
System.out.println("annotation = "+annotation);
}
}
}
}
这个最简单了,来试试@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ClassAnnotation {
}
@ClassAnnotation
public class Test3 {
class Test4 extends Test3 {
}
public static void main(String[] args) {
//Annotation[] declaredAnnotations = Test4.class.getDeclaredAnnotations();
Annotation[] annotations = Test4.class.getAnnotations();
if (annotations != null) {
for (Annotation annotation : annotations) {
System.out.println("annotation = "+annotation);
}
}
}
}
注解添加了@Inherited
,因此可以被子类继承,这时子类通过getDeclaredAnnotations()
取不到父类的注解(这个只获取当前类的),需要调用getAnnotations()
可以获取所有的注解包括父类的。
反射获取注解信息
上面代码中已经涉及到了,这里再列一下:
// 获取成员变量上的注解信息
Field[] fields = object.getClass().getDeclaredFields();
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
// 获取方法上的注解信息
Method[] methods = Test2.class.getDeclaredMethods();
if (method.isAnnotationPresent(Permission.class)) {
Permission permission = method.getAnnotation(Permission.class);
}
// 获取类上的注解信息
Annotation[] declaredAnnotations = Test3.class.getDeclaredAnnotations(); // 取不到父类的注解(这个只获取当前类的)
Annotation[] annotations = Test4.class.getAnnotations(); // 可以获取所有的注解包括父类的。
注意 getField
和 getDeclearedField
的区别:
getField
:获得自己 + 父类的成员(不包括private
,只能是public
)getDecleredFiled
:只能获得自己的成员(不包括父类,所有作用域)
getDeclaredAnnotations
和 getAnnotations
的区别:
getDeclaredAnnotations
只能获取当前类的注解,取不到父类的注解getAnnotations
可以获取当前类的所有的注解,包括父类的。