Java注解详细说明
今天面试过程中遇到了一个问题:谈谈注解的底层实现。由于本人只写过几个自定义注解,在复习过程中也忽略了这一部分,所以只回答了使用过一些自定义注解。然后面试官又问了一些反射的问题。面试结束经过百度后才知道,注解的底层实现就是用到了反射。所以今天来补上这一部分内容。
首先,注解是没有行为的,只能有数据。让其实现某些行为必须有实例(也就是代理对象),再用反射技术实现某些行为。
通过对一个注解进行反编译,我们可以在反编译代发中发现
public interface extends java.lang.annotation.Annotation
,我们可以得知,注解就是一个继承自
java.lang.annotation.Annotation
的接口。也就是说,注解就是一个接口。
那么接口又是怎么样设置的属性呢?简单来说就是Java通过动态代理的方式来生成了一个接口的实例。这样对该代理的实例的属性赋值后,我们就可以通过反射,在程序运行时获取到注解的配置信息了。
下面再来看一块代码
@Override
public String toString() {
return "xxxxx";
}
上面的代码重写了toString方法并使用了@Override注解。但是即使它不使用此注解,程序也会正常执行。
所以注解也称为源代码的元数据。也就是说他只是一个附带品而已,它本身没有任何作用,只有外部程序(编译器或VM)解析它,才会引起它的作用。比如@Override的作用是告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。如果不小心拼写错误,例如将toString()写成了toStrring(){double r},而且没有使用@Override注解,那程序依然能编译运行。但实际的执行结果肯定与预期不符。这也是注解的一大功能,就是有助于程序的阅读,
一个注解基本由
Annotation
、
ElementType
、
RetentionPolicy
三个类组成,
Annotation
是接口,其余两个是枚举。 所有的注解都基于这三个类,所谓的元注解也是用这三个类生成的,只不过可修饰的元素(ElementType)为ANNOTATION_TYPE。
下面简要的介绍一下元注解
元注解就是用来描述注解的注解,比如“@Target”元注解,它的作用是用来说明MethodInfo这个注解只能用于对方法进行注解:
@Target(ElementType.METHOD)
public @interface MethodInfo {
...
}
1. Retention
这个元注解表示一个注解会被保留到什么时候,比如以下代码表示Developer注解会被保留到运行时(也就是说在运行时依然能发挥作用):
@Retention(RetentionPolicy.RUNTIME)
public @interface Developer {
String value();
}
我们在使用@Retention时,后面括号里的内容即表示它的取值,从以上定义我们可以看到,取值的类型为RetentionPolicy,这是一个枚举类型,它可以取以下值:
- SOURCE:表示在编译时这个注解会被移除,不会包含在编译后产生的class文件中;
- CLASS:表示这个注解会被包含在class文件中,但在运行时会被移除;
- RUNTIME:表示这个注解会被保留到运行时,在运行时可以JVM访问到,我们可以在运行时通过反射解析这个注解。
2. Documented
当一个注解被@Documented元注解所修饰时,那么无论在哪里使用这个注解,都会被Javadoc工具文档化。
3. Inherited
4. Target
- TYPE:表示可以用来修饰类、接口、注解类型或枚举类型;
- PACKAGE:可以用来修饰包;
- PARAMETER:可以用来修饰参数;
- ANNOTATION_TYPE:可以用来修饰注解类型;
- METHOD:可以用来修饰方法;
- FIELD:可以用来修饰属性(包括枚举常量);
- CONSTRUCTOR:可以用来修饰构造器;
- LOCAL_VARIABLE:可用来修饰局部变量。