一、Java注解的定义
1.定义:注解是代码的特殊标志,使用于包、类、接口、字段、方法参数、局部变量等。注解可以作用于编译检查、编译时、运行时。
注意:注解不是注释。
二、Java注解的分类及使用
1.Java自带的标准注解
包括@Override、@Deprecated和@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。
public class Test {
@Override
public String tostring() {
return "override it";
}
}
2.元注解
元注解是用于定义注解的注解,包括@Retention用于标明注解被保留的阶段;@Target用于标明注解使用的范围;@Inherited用于标明注解可继承;@Documented用于标明是否生成javadoc文档;
3.自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。
// 创建Test自定义注解,声明作用于类并保留到运行时,默认值为default
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String value() default "default";
}
三、Java注解的作用与原理
1.注解的作用
a.生成文档,通过代码里标识的元数据生成javadoc文档,例如@Documented;
b.编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证,例如@Override、@Deprecated和@SuppressWarnings;
c.编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码;
d.运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例,如下代码所示
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface MyAnno {
String value();
}
// 注解的使用
@MyAnno("class")
public class MyClass {
@MyAnno("feild")
private String str;
@MyAnno("method")
public void method() { }
}
// 反射获取注解 使用Annotation接口
public class Test {
public static void main(String[] args) throws Exception {
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();
// 获取对象上的注解
MyAnno anno = clazz.getAnnotation(MyAnno.class);
System.out.println(anno.value());
// 获取属性上的注解
Field field = clazz.getDeclaredField("str");
anno = field.getAnnotation(MyAnno.class);
System.out.println(anno.value());
// 获取方法上的注解
Method method = clazz.getMethod("method");
anno = method.getAnnotation(MyAnno.class);
System.out.println(anno.value());
}
}
2.注解的原理
原理:编译器将注解信息添加到class结构的属性中,同时注解可以作用于编译检查、编译时和运行时。
利用下面例子分析其原理。
@Test("annotationTest01 ")
public class AnnotationTest {
public void test(){
}
}
编译器:java源码通过编译器转换为class字节码。在此过程中,编译器也会处理注解符号并附加到class结构。根据JVM的规范,class结构是严格有序的,保存到class结构的attributes属性是唯一可以附加信息的方式。类、字段、方法在class结构中都有特定的表结构,同时都有attributes属性。因此,注解作用在类、字段、方法,就是编译器将注解信息存放到类、字段、方法的attributes属性。例如,AnnotationTest.java被编译成AnnotationTest.class,其中包含一个RuntimeVisibleAnnotations属性。@Test("test")的键值对value=test就是添加到类的RuntimeVisibleAnnotations属性。
JVM:类加载过程中,加载AnnotationTest.class时,将RuntimeVisibleAnnotations属性值保存到AnnotationTest的Class对象。因此,AnnotationTest.class.getAnnotation(Test.class)获取到Test注解对象,从而获取到Test注解中的属性值annotationTest01。
说明:Test注解对象是一个继承Annotation接口的接口,即@Test实质是“public interface Test extends Annotation”。当运行AnnotationTest.class.getAnnotation(Test.class)时,JDK会通过动态代理生成一个实现了Test接口的对象,并把将RuntimeVisibleAnnotations属性值设置进此对象中,从而调用value()方法就可以获取到属性值annotationTest01。