1.什么是注解?
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响代码的执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。通俗的将:可以完全将注解当做生活中我们对人或物贴的标签。
2.注解的原理
注解本质是一个继承了 Annotation 的特殊接口,其具体实现类是 Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是 Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用 AnnotationInvocationHandler 的 invoke 方法。该方法会从 memberValues 这个 Map 中索引出对应的值。而 memberValues 的来源是 Java 常量池。
——摘自《注解Annotation实现原理与自定义注解例子》
3.注解的作用
编译检查:Annotation 具有"让编译器进行编译检查的作用"。
例如,@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用。
在反射中使用 Annotation:
Java
的AnnotatedElement
接口中有getAnnotation()
等获取注解的方法。
而Method
,Field
,Class
,Package
等类均实现了这个接口,因此均有获取注解的能力。根据 Annotation 生成帮助文档:通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中。
能够帮忙查看代码:通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。
4.元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。或者可以理解为:元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
元注解所在包
java.lang.annotation
五大元注解
@Documented
Documented:“已记录”的意思,当@Documented应用到一个注解上时,表示要将注解信息添加在 Java 文档,即 Javadoc 中;在@Documented缺省的情况下表示注解信息是不出现在 javadoc 中的。
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Inherited
Inherited:“遗传,继承”的意思,@Inherited 定义了一个注解与子类的关系;如果一个超类被带有 @Inherited 的注解修饰,那么对于该超类,它的子类如果没有被任何注解应用的话,那么这个子类就继承了超类的注解。
例如:
注解Test被@Inherited修饰 @Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test{} 超类: @Test public class A {} 子类: public class B extends A {} 说明: 注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
@Repeatable
Repeatable:“可重复的”的意思,当@Repeatable修饰一个注解时,表明该注解可以重复应用在同一个类上,即该注解可以取多个值;如:一个人既是程序员,又是产品经理,同时也是画家。
例如:
//注解1: @interface Persons { Person[] value(); } //注解2: @Repeatable(Persons.class) @interface Person { String role default ""; } //有使用@Repeatable()时的使用 @Person(role="programmer") @Person(role="PM") @Person(role="artist") public class SuperMan { } //没使用@Repeatable()时的使用,即注解2 @Person去掉@Repeatable元注解 @Persons({ @Person(role="programmer"), @Person(role="PM"), @Person(role="artist")}) public class SuperMan { }
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
注意:按照规定,如果使前面的 Persons 里面可以重复调用某个注解,则 Persons 必须有一个 value 的属性,且属性类型必须为被 @Repeatable 注解的 Person。
@Retention
Retention:“保留”的意思,当 @Retention 应用到一个注解上时,用来定义该注解的生命周期,即存活时间。
若没有 @Retention,则默认是 RetentionPolicy.CLASS。
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 注解只在源码阶段保留,在编译器完整编译之后,它将被丢弃忽视;
* 如:@Override, @SuppressWarnings
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中;
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
* 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们;
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
@Target
target:“目标”的意思,当@Target应用到一个注解上时,用来限制该注解的应用场景,即应用范围。
若没有 @Target,则说明该注解可以用于任何地方。
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration
* 对类、接口、枚举进行注解;
*/
TYPE,
/** Field declaration (includes enum constants)
* 对属性、成员变量、成员对象(包括 enum 实例)进行注解;
*/
FIELD,
/** Method declaration 对方法进行注解;*/
METHOD,
/** Formal parameter declaration 对描述参数进行注解;*/
PARAMETER,
/** Constructor declaration 对构造方法进行注解;*/
CONSTRUCTOR,
/** Local variable declaration 对局部变量进行注解;*/
LOCAL_VARIABLE,
/** Annotation type declaration 对注解进行注解;*/
ANNOTATION_TYPE,
/** Package declaration 对包进行注解;*/
PACKAGE,
/**
* Type parameter declaration
* 对类型参数声明进行注解
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
* 对使用类型进行注解
* @since 1.8
*/
TYPE_USE
}
5.注解的属性
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
支持的元素类型
- 8种基本数据类型(
byte
,short
,char
,int
,long
,float
,double
,boolean
)String
Class
enum
- 注解类型
- 数组(所有上边类型的数组)
使用时,无默认值的元素必须传值。
有两种比较特别的注解
- 标记注解 : 注解中没有任何元素,使用时直接是
@XxxAnnotation
, 不需要加括号- 单值注解 : 注解只有一个元素,且名字为
value
,使用时直接传值,不需要指定元素名,如:@XxxAnnotation(100)
6.java预置注解
- @Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
7.自定义注解及使用
注解的格式
修饰符 @interface 注解名 {
注解元素类型type elementName();
注解元素类型type elementName() default value; // 带默认值
}
- 自定义注解类型定义为 @interface,所有的注解会自动继承 java.lang.Annotation 这一接口,而且不能再去继承其他的类或接口;定义 Annotation 时,@interface 是必须的。
- 注解元素只能用 public 或 default 两个关键字修饰;
- 注解元素只能用基本类型:byte, short, char, int, long, float, double, boolean,以及 String, Enum, Class, Annotations 等数据类型,以及这些类型的数组;
- 要获取类方法和字段的注解信息,必须通过 Java 的反射技术;
- 注解也可以不定义成员变量;
- 自定义注解需要使用元注解进行编写;
示例1
public enum Status {
GOOD,
BAD
}
@Target(ElementType.ANNOTATION_TYPE)
public @interface MyAnnotation1 {
int val();
}
@Target(ElementType.TYPE)
public @interface MyAnnotation2 {
boolean boo() default false;
Class<?> cla() default Void.class;
Status enu() default Status.GOOD;
MyAnnotation1 anno() default @MyAnnotation1(val = 1);
String[] arr();
}
//使用时,无默认值的元素必须传值
@MyAnnotation2(
cla = String.class,
enu = Status.BAD,
anno = @MyAnnotation1(val = 2),
arr = {"a", "b"})
public class MyTest1 {
}
示例2
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface MyAnnotation {
String value();
String value1() default "annotation";
}
@MyAnnotation("class")
public class MyClass {
@MyAnnotation("field")
private String str;
@MyAnnotation("method")
public void method() { }
}
public class MyTest {
public static void main(String[] args) throws Exception {
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();
// 获取类上的注解值
MyAnnotation anno = clazz.getAnnotation(MyAnnotation.class);
System.out.println(anno.value());
// 获取属性上的注解
Field field = clazz.getDeclaredField("str");
anno = field.getAnnotation(MyAnnotation.class);
System.out.println(anno.value());
// 获取方法上的注解
Method method = clazz.getMethod("method");
anno = method.getAnnotation(MyAnnotation.class);
System.out.println(anno.value());
}
}
//class
//feild
//method
示例3
//定义水果供应商注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/*供应商编号*/
public int id() default -1;
/*供应商名称*/
public String name() default "";
/*供应商地址*/
public String address() default "";
}
//定义Apple类使用水果供应商注解
@FruitProvider(id = 1728, name = "why", address = "shenzhen")
public class Apple {
private int appleID;
private String appleProvidername;
private String appleprovideraddress;
public int getAppleID() {
return appleID;
}
public void setAppleID(int appleID) {
this.appleID = appleID;
}
public String getAppleProvidername() {
return appleProvidername;
}
public void setAppleProvidername(String appleProvidername) {
this.appleProvidername = appleProvidername;
}
public String getAppleprovideraddress() {
return appleprovideraddress;
}
public void setAppleprovideraddress(String appleprovideraddress) {
this.appleprovideraddress = appleprovideraddress;
}
}
//注解处理器:利用反射处理注解
public class FruitInfoUtil {
public static Apple getAApple(Class<?> clazz) throws Exception{
FruitProvider fb = clazz.getAnnotation(FruitProvider.class);//通过反射获取处理注解
//通过newInstance()生成Apple实例,利用反射的结果进行设置
Apple ap = (Apple)clazz.newInstance();
ap.setAppleID(fb.id());
ap.setAppleProvidername(fb.name());
ap.setAppleprovideraddress(fb.address());
return ap;
}
}
public class Main {
public static void main(String[] args) throws Exception{
Apple a = FruitInfoUtil.getAApple(Apple.class);
System.out.println("苹果商的ID为:"+a.getAppleID());
System.out.println("苹果商的名字为:"+a.getAppleProvidername());
System.out.println("苹果商的地址为:"+a.getAppleprovideraddress());
}
}