文章目录
注解(Annotation)
第一部分:Annotation简单说明
Java注解(Annotation),又称Java标注,是从JDK1.5开始引入的一种机制。
Java语言中的类、方法、变量、参数和包等都可以被Java注解进行标注,和Javadoc不懂,Java标注可以通过反射获取其标注的内容;在编译期生成类文件的同时,标注就被嵌入到字节码中,Java虚拟机可以保留主键内容,在运行时可以获取到标注的内容。
第二部分:Annotation整体架构
有关Annotation架构,可以先看看下图:
上图分为左右两部分
- 左侧部分:围绕Annotation、RetentionPlicy、ElementType这三个类的对应关系,如下:
- Annotation和RetentionPlicy是1:1的关系,即1个Annotation对应1个RetentionPolicy,具体就是1个Annotation对象,只有唯一的RetentionPolicy属性
- Annotation和ElementType是1:N的关系,即1个Annotaion对应多个ElementType,具体就是1个Annotation对象,可以有多个ElementType属性
- 右侧部分:
- 围绕Annotation的继承体系展开的,Annotation有许多实现类,包括:Deprecated、Documented、Inherited、Override、Retention、Target等
第三部分:Annotation组成部分
Annotation组成概述
Annocation组成围绕:Annotation、RetentionPlicy、ElementType这个三个类,如下如:
Annotation组成明细
Annotation、RetentionPlicy、ElementType类内容如下:
-
Annotation
package java.lang.annotation; /** * @author Josh Bloch * @since 1.5 */ public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); Class<? extends Annotation> annotationType(); }
-
RetentionPlicy
package java.lang.annotation; /** * @author Joshua Bloch * @since 1.5 */ public enum RetentionPolicy { /*Annotation信息仅存在与编译器处理期(编译期),编译器处理完后就没有该Annotation信息了*/ SOURCE, /*编译器将Annotaion信息存储到class文件中,它是默认行为*/ CLASS, /*编译器将Annotation信息存储到class文件中,并且可以由JVM加载*/ RUNTIME }
-
ElementType
package java.lang.annotation; /** * @author Joshua Bloch * @since 1.5 */ public enum ElementType { TYPE, /*类、接口(包含注释类型)或枚举声明*/ FIELD, /*字段(包含枚举常量)*/ METHOD, /*方法声明*/ PARAMETER, /*参数声明*/ CONSTRUCTOR, /*构造器声明*/ LOCAL_VARIABLE, /*局部变量声明*/ ANNOTATION_TYPE,/*注释类型声明*/ PACKAGE /*包声明*/ }
说明:
-
Annotation是一个接口类,包含4个抽象方法
-
RetentionPolicy是Enum枚举类,它用来指定Annotation的策略,即:不同的RetentionPolicy类型的Annotation作用域不同,RetentionPolicy三种不同的策略
-
SOURCE
意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如," @Override" 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法,代表子类重写父类的方法;并且在编译期间会进行语法检查!编译器处理完后,"@Override" 就没有任何作用了。
-
CLASS
意味着:编译器将 Annotation 存储于class 文件中,它是 Annotation 的默认行为
-
RUNTIME
意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入
-
-
ElementType是Enum枚举类,它用来指定Annotation的类型,即:Annotation用来修饰什么,比如ElementType.TYPE表示Annotation只能用来修改类、接口等;ElementType.METHOD表示Annotation只能用来修饰方法;如果是多个值{ElementType.TYPE,ElementType.METHOD}表示Annotation既可以修饰类,又可以修饰方法。
第四部分:Annotation实现部分
Annotation实现类的语法定义
自定义Annotation实现:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
使用自定义的Annotation:
package annotation;
@MyAnnotation
public class Test {
}
说明:
上述我们自定义了一个Annotation,MyAnnotation,这样我们就可以在Test这个类中使用了。
(01) @interface
使用@interface定义注解时,意味着它实现了java.lang.annotation.Annotation接口,即该注解就是一个Annotation。
定义Annotation时,@interface是必须的。
注意:它和我们通常的implemented实现接口的方法不同。Annotation接口的实现细节都由编译器完成。通过@interface定义注解后,该注解不能继承其他的注解或接口。
(02) @Documented
类和方法的Annotation在缺省情况下是不出现在javadoc中的。如果使用@Documented修饰该Annotation,则表示它可以出现在javadoc中。
定义Annotation时,@Documented可有可无;若没有定义,则Annotation不会出现在javadoc中。
(03) @Target(ElementType.TYPE)
前面我们说过,ElementType 是Annotation的类型属性。而@Target的作用,就是来指定Annotation的类型属性。
@Target(ElementType.TYPE) 的意思就是指定该Annotation的类型是ElementType.TYPE。这就意味着,MyAnnotation1是来修饰“类、接口(包括注释类型)或枚举声明”的注解。
定义Annotation时,@Target可有可无。若有@Target,则该Annotation只能用于它所指定的地方;若没有@Target,则该Annotation可以用于任何地方。
(04) @Retention(RetentionPolicy.RUNTIME)
前面我们说过,RetentionPolicy 是Annotation的策略属性,而@Retention的作用,就是指定Annotation的策略属性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该Annotation的策略是RetentionPolicy.RUNTIME。这就意味着,编译器会将该Annotation信息保留在.class文件中,并且能被虚拟机读取。
定义Annotation时,@Retention可有可无。若没有@Retention,则默认是RetentionPolicy.CLASS。
Annotation的Java自带的实现类
Java自带定义了一套注解,共有7个,3个在java.lang包下,4个在java.lang.annotation包中
Java自带的注解可以分为作用在代码的注解和作用在其他注解的注解(元注解)
- 作用在代码的注解
- @Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated:标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings:指示编译器去忽略注解中声明的警告。
- 作用在其他注解的注解(元注解)
- @Retention:标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented:标记这些注解是否包含在用户文档中
- @Target: 标记这个注解应该是哪种 Java 成员。
- @Inherited:标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
从Java7开始,增加了 3个注解
- @SafeVarargs:Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface:Java 8 开始支持,标识一个匿名函数或函数式接口
- @Repeatable:Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
-
@Override
定义如下:package java.lang; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
说明:
- @interface:用来修饰Override,意味着Override实现了 java.lang.annotation.Annotation 接口;即Override是一个注解。
- @Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Target:指定了Override注解的类型是
METHOD
,意味着@Override注解只能用来标注“方法” - @Retention:指定了Override注解的策略是RetentionPolicy.SOURCE,意味着注解仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了
实例:
以下的实例中,父类ReflectFather中有一个testFather方法,子类ReflectDemo如果重写了testFather方法,则会在子类重写的方法上用@Override注解进行标注,具体如下:
package helloword; /** * 父类 */ public class ReflectFather { public void testFather(String name){ } } //----------------------------------------------------------------- package helloword; /** * 子类 */ public class ReflectDemo extends ReflectFather{ @Override public void testFather(String name) { super.testFather(name); } }
-
@Deprecated
的定义如下:package java.lang; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE}) public @interface Deprecated { }
说明:
- @interface:用来修饰Deprecated,意味着Deprecated实现了 java.lang.annotation.Annotation 接口;即Deprecated是一个注解。
- @Deprecated: 它的作用是所标注内容,不再被建议使用。
- @Retention:指定了Deprecated注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 Deprecated的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了Deprecated注解的类型是ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE,意味着@Deprecated可以用来标注构造器、字段、局部变量、方法、包、参数、类或接口
实例:
public class ReflectDemo extends ReflectFather{ @Deprecated private String name ; public ReflectDemo() { } @Deprecated public ReflectDemo(String name) { this.name = name; } @Override @Deprecated public void testFather(String name) { super.testFather(name); } public static void main(String[] args) { ReflectDemo reflectDemo = new ReflectDemo(); //java不建议使用该构造方法 reflectDemo = new ReflectDemo(""); //java不建议使用该方法 reflectDemo.testFather(""); //java不建议使用该字段 String name = reflectDemo.name; //java不建议使用该类 Son son = new Son(); @Deprecated int age = 18; System.out.println(age); } @Deprecated static class Son{ } }
-
@SuppressWarnings
的定义如下:package java.lang; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
说明:
-
@interface:用来修饰SuppressWarnings,意味着SuppressWarnings实现了java.lang.annotation.Annotation 接口;即SuppressWarnings是一个注解。
-
@SuppressWarnings: 它的作用是指示编译器去忽略注解中声明的警告。"@SuppressWarnings(value={“deprecation”, “unchecked”})" 表示对“它所标注的内容”中的 “SuppressWarnings不再建议使用警告”和“未检查的转换时的警告”保持沉默。
-
@Retention:指定了SuppressWarnings注解的策略是RetentionPolicy.SOURCE,意味着注解仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了
-
@Target:指定了SuppressWarnings注解的类型是ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,意味着@Deprecated可以用来标注构造器、字段、局部变量、方法、包、参数、类或接口
-
String[] value(); 意味着,SuppressWarnings能指定参数
deprecation -- 使用了不赞成使用的类或方法时的警告 unchecked -- 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。 fallthrough -- 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。 path -- 在类路径、源文件路径等中有不存在的路径时的警告。 serial -- 当在可序列化的类上缺少 serialVersionUID 定义时的警告。 finally -- 任何 finally 子句不能正常完成时的警告。 all -- 关于以上所有情况的警告。
实例:
-
-
@Retention
的定义如下:package java.lang.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) public @interface Retention { RetentionPolicy value(); }
说明:
- @interface:用来修饰Retention,意味着Retention实现了java.lang.annotation.Annotation 接口;即Retention是一个注解。
- @Retention: 它的作用是指示编译器去忽略注解中声明的警告。
- @Retention:指定了Retention注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 Retention的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了Retention注解的类型是ElementType.ANNOTATION_TYPE,意味着@Retention只能被用来标注“Annotation类型”
实例:
package java.lang.annotation; @Documented @Retention(RetentionPolicy.RUNTIME)//在注解上使用@Retention @Target({ElementType.ANNOTATION_TYPE}) public @interface Retention { RetentionPolicy value(); } package java.lang; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE)//在注解上使用@Retention public @interface SuppressWarnings { String[] value(); }
-
@Documented
的定义如下:package java.lang.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) public @interface Documented { }
说明:
- @interface:用来修饰Documented,意味着Documented实现了java.lang.annotation.Annotation 接口;即Documented是一个注解。
- @Documented: 它的作用是标记这些注解是否包含在用户文档中。
- @Retention:指定了Documented注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 Documented的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了Documented注解的类型是ElementType.ANNOTATION_TYPE,意味着@Documented只能被用来标注“Annotation类型”
实例:
如上图所示:如果@Documented使用在类上时,对应的IDE就会提示“@Documented not applicable to type”
-
@Target
的定义如下:package java.lang.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) public @interface Target { ElementType[] value(); }
说明:
- @interface:用来修饰Target,意味着Target实现了java.lang.annotation.Annotation 接口;即Target是一个注解。
- @Target: 它的作用是指定注解类型。
- @Retention:指定了Target注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 Target的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了Target注解的类型是ElementType.ANNOTATION_TYPE,意味着@Target只能被用来标注“Annotation类型”
实例:
package java.lang.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE})//指定注解的类型 public @interface Target { ElementType[] value(); }
-
@Inherited
的定义如下:package java.lang.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
说明:
- @interface:用来修饰Inherited,意味着Inherited实现了java.lang.annotation.Annotation 接口;即Inherited是一个注解。
- @Inherited: 它的作用是标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。
- @Retention:指定了Inherited注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 Inherited的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了Inherited注解的类型是ElementType.ANNOTATION_TYPE,意味着@Inherited只能被用来标注“Annotation类型”
实例:
/*自定义注解,没有@Inherited标注进行标注自定义注解*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) //@Inherited public @interface InheritedAnnocation { } /** * 被自定义注解@InheritedAnnocation标注的类 */ @InheritedAnnocation public class ReflectFather { } /** * 继承自ReflectFather */ public class ReflectDemo extends ReflectFather{ public static void main(String[] args) { System.out.println("父类ReflectFather被注解InheritedAnnocation标注了?"+ReflectFather.class.isAnnotationPresent(InheritedAnnocation.class)); System.out.println("子类ReflectDemo被注解InheritedAnnocation标注了?"+ReflectDemo.class.isAnnotationPresent(InheritedAnnocation.class)); } } 执行输入结果: 父类ReflectFather被注解InheritedAnnocation标注了?true 子类ReflectDemo被注解InheritedAnnocation标注了?true
/*自定义注解,有@Inherited标注进行标注自定义注解*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface InheritedAnnocation { } /** * 被自定义注解@InheritedAnnocation标注的类 */ @InheritedAnnocation public class ReflectFather { } /** * 继承自ReflectFather */ public class ReflectDemo extends ReflectFather{ public static void main(String[] args) { System.out.println("父类ReflectFather被注解InheritedAnnocation标注了?"+ReflectFather.class.isAnnotationPresent(InheritedAnnocation.class)); System.out.println("子类ReflectDemo被注解InheritedAnnocation标注了?"+ReflectDemo.class.isAnnotationPresent(InheritedAnnocation.class)); } } 执行输入结果: 父类ReflectFather被注解InheritedAnnocation标注了?true 子类ReflectDemo被注解InheritedAnnocation标注了?false
-
@SafeVarargs
的定义如下:package java.lang; import java.lang.annotation.*; /** *@since 1.8 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface SafeVarargs {}
说明:
- @interface:用来修饰SafeVarargs,意味着SafeVarargs实现了java.lang.annotation.Annotation 接口;即SafeVarargs是一个注解。
- @SafeVarargs: 它的作用是忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @Retention:指定了SafeVarargs注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 SafeVarargs的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了SafeVarargs注解的类型是ElementType.CONSTRUCTOR, ElementType.METHOD,意味着@SafeVarargs可以用来标注构造器、方法
-
@FunctionalInterface
的定义如下:package java.lang; import java.lang.annotation.*; /** * @since 1.8 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
说明:
- @interface:用来修饰FunctionalInterface,意味着FunctionalInterface实现了java.lang.annotation.Annotation 接口;即FunctionalInterface是一个注解。
- @FunctionalInterface: 它的作用是标识一个匿名函数或函数式接口。
- @Retention:指定了FunctionalInterface注解的策略是RetentionPolicy.RUNTIME,意味着,编译器会将 FunctionalInterface的信息保留在 .class 文件中,并且能被虚拟机读取。
- @Target:指定了FunctionalInterface注解的类型是ElementType.TYPE,意味着@FunctionalInterface只能用来标注类或接口
实例:
package java.lang; @FunctionalInterface public interface Runnable { public abstract void run(); }
-
@Repeatable
的定义如下:package java.lang.annotation; /** * @since 1.8 */ @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(); }
第五部分:Annotation应用部分
Annotation在反射中相关的接口,我们可以在反射中解析并使用这些注解
package annotation;
import java.lang.annotation.*;
/**
*自定义注解
*/
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "你好";
String method() default "POST";
}
/**
* 测试类
*/
public class ReflectDemo{
public static void main(String[] args) {
getMyAnnotation();
}
@MyAnnotation
@Deprecated
public void test1(){
}
@MyAnnotation(value = "方法test2",method = "GET")
public void test2(){
}
public static void getMyAnnotation(){
try {
Class cl = Class.forName("helloword.ReflectDemo");
Method test1 = cl.getMethod("test1");
MyAnnotation myAnnotation1 = test1.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation1.value());
System.out.println(myAnnotation1.method());
System.out.println("---------------------------");
Method test2 = cl.getMethod("test2");
MyAnnotation myAnnotation2 = test2.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation2.value());
System.out.println(myAnnotation2.method());
System.out.println("---------------------------");
//获取方法上所有注解
Annotation[] annotations = test1.getAnnotations();
for(Annotation annotation:annotations){
System.out.println(annotation);
}
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
输出: