Java基础-注解
原文:CSDN(历史文章导入,当前状态为草稿)
注解是用来干什么的
Section titled “注解是用来干什么的”注解的汉语意思: 用浅近的文字解释艰深的词句
注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包,类,接口,字段,方法参数,局部变量进行注解.
它有什么作用
Section titled “它有什么作用”主要作用是下面四个方面
- 生成文档
通过代码里标识的元数据生成javaDoc文档 - 编译检查
通过代码里标识的元数据让编译器在编译期间进行检查验证 - 编译时动态处理
编译时通过代码里标识的元数据动态处理,例如动态生成代码 - 运行时动态处理
运行时通过底阿妈里标识的元数据动态处理,例如使用反射注入实例
注解的常见分类
Section titled “注解的常见分类”- Java自带的标准注解
用于标明重写某个方法,某个类或方法过时,表明要忽略的警告,用这些注解表明后编译器就会 进行检查 - 元注解
用于定义注解的注解 - 自定义注解
根据自己的需求定义注解,并可用元注解对自定义注解进行注解
@Override
Section titled “@Override”表示当前的方法定义将覆盖父类中的方法
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}这个注解可以用来修饰方法,并只在编译期有效,编译期后class文件就不存在了
@Deprecated
Section titled “@Deprecated”表示代码被弃用,如果使用了被@Deprecated注解的代码编译器会发出警告
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}- 它会被文档化
- 可以保留到运行时
- 可以修饰构造方法,属性,局部变量,方法,包,参数,类型
@SuppressWarnings
Section titled “@SuppressWarnings”关闭编译器警告信息
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings { String[] value();}- 可以修饰类型,属性,方法,参数,构造器,局部变量
- 只能存活在源码
- 取值为String[]
它可以取的值如下所示:
| 参数 | 作用 |
|---|---|
| all | 抑制所有警告 |
| deprecation | 抑制启用注释的警告 |
| finally | 抑制finally模块没有返回的警告 |
| null | 忽略对null的操作 |
| unused | 抑制没被使用过的代码的警告 |
| 等等 |
@Target
Section titled “@Target”描述注解的使用范围(即被修饰的注解可以在什么地方使用)
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Target { /** * Returns an array of the kinds of elements an annotation interface * can be applied to. * @return an array of the kinds of elements an annotation interface * can be applied to */ ElementType[] value();}- 可被文档化
- 可以保留到运行时
- 只能在注解类上使用
- value值在ElementType中
ElementType
Section titled “ElementType”public enum ElementType {
TYPE, // 类、接口、枚举类
FIELD, // 成员变量(包括:枚举常量)
METHOD, // 成员方法
PARAMETER, // 方法参数
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类
PACKAGE, // 可用于修饰:包
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}@Retention&&@RetentionTarget
Section titled “@Retention&&@RetentionTarget”描述注解保留的时间范围(即被描述的注解在它所修饰的类中可以被保留到何时)
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Retention { RetentionPolicy value();}- 可被文档化
- 可以保留到运行时
- 只能在注解类上使用
- value值在RetentionPolicy 中
RetentionPolicy
Section titled “RetentionPolicy”public enum RetentionPolicy { SOURCE, // 源文件保留 CLASS, // 编译期保留,默认值 RUNTIME // 运行期保留,可通过反射去获取注解信息}我们可以通过执行javap -verbose RetentionTest获取到RetentionTest的class字节码内容如下.
{ public retention.RetentionTest(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0
public void sourcePolicy(); flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 7: 0
public void classPolicy(); flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 11: 0 RuntimeInvisibleAnnotations: 0: #11()
public void runtimePolicy(); flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 15: 0 RuntimeVisibleAnnotations: 0: #14()}我们可以得到下面的两个结论:
- 编译期没有记录下sourcePolicy()方法的注解信息
- 编译期分别使用了
RuntimeInvisibleAnnotations,RuntimeVisibleAnnotations属性去记录了classPolicy()方法和runtimePolicy()方法的注解信息
@Documented
Section titled “@Documented”描述使用javaDoc工具为类生成帮助文档时是否要保留其注解信息
@Documented@Target({ElementType.TYPE,ElementType.METHOD})public @interface TestDocAnnotation {
public String value() default "default";}- 可被生成文档
- 使用范围是类,接口,枚举类,成员方法
@Inherited
Section titled “@Inherited”被他修饰的注解具有继承性.
如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注释
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Inherited {}- 可被生成文档
- 可被保留到运行时
- 只能用于注解类
定义@Inherited注解
@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.METHOD})public @interface TestInheritedAnnotation { String [] values(); int number();}使用这个注解
@TestInheritedAnnotation(values = {"value"}, number = 10)public class Person {}
class Student extends Person{ @Test public void test(){ Class clazz = Student.class; Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.toString()); } }}输出内容
xxxxxxx.TestInheritedAnnotation(values=[value], number=10)即使Student类没有显示地被注解@TestInheritedAnnotation,但是它的父类Person被注解,而且@TestInheritedAnnotation被@Inherited注解,因此Student类自动有了该注解.
Repeatable(重复注解)
Section titled “Repeatable(重复注解)”允许在同一申明类型(类,属性,方法)多次使用同一注解
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Repeatable { Class<? extends Annotation> value();}- 可被生成文档
- 可以保留至运行时
- 只能作用域注解类
JDK8之前
Section titled “JDK8之前”JDK8之前有重复使用注解的解决方案,但是可读性不好
方案: 由另一个注解来存储重复注解,在使用的时候,用存储注解Authorities来扩展重复注解
public @interface Authority { String role();}
public @interface Authorities { Authority[] value();}
public class RepeatAnnotationUseOldVersion {
@Authorities({@Authority(role="Admin"),@Authority(role="Manager")}) public void doSomeThing(){ }}JDK8之后
Section titled “JDK8之后”方案: 创建重复注解时,加上@Repeatable,指向存储注解Authorities,在使用的时候,可以直接重复使用Authority注解.
@Repeatable(Authorities.class)public @interface Authority { String role();}
public @interface Authorities { Authority[] value();}
public class RepeatAnnotationUseNewVersion { @Authority(role="Admin") @Authority(role="Manager") public void doSomeThing(){ }}Native
Section titled “Native”使用@Native注解修饰成员变量,表示这个变量可以被本地代码引用,常常被代码生成工具使用,了解即可.
注解与反射接口
Section titled “注解与反射接口”我们如果想自定义注解,那么就必须先了解注解与反射接口的这部分内容.
反射包java.lang.reflect下的AnnotatedElement接口提供这些方法.
注意:只有注解被定义为RUNTIME后,该注解才能是运行时可见,当class文件被装载时,被保存在class文件中的Annotation才会被虚拟机读取.
AnnotatedElement接口是所有程序元素(Class,Method,Constructor)的父接口.
Class类public final class Class<T> implements Serializable, GenericDeclaration, Type, AnnotatedElement, TypeDescriptor.OfField<Class<?>>, Constable {xxxx}Method类public final class Method extends Executable {xxx}public abstract sealed class Executable extends AccessibleObject{xxx}public class AccessibleObject implements AnnotatedElement {xxx}
Constructor类public final class Constructor<T> extends Executable {xxx}和上面Method的继承路径是是一样的,不赘述了.boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
判断该元素上是否包含指定类型的注解,存在则返回true,否则返回false.
(此方法会忽略注解对应的注解容器)<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回该程序元素上存在的,制定类型的注解,如果该类型的注解不存在,则返回null<Annotation[] getAnnotations()
返回该程序元素上存在的所有注解,若没有注解,返回长度为0的数组<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
返回该程序元素上存在的,指定类型的注解数据.若没有,返回长度为0的数组
该方法调用者可以随意修改返回数据,不会对其他调用者返回数组产生影响.
(会检测注解对应的重复注解容器)<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass
返回直接存在此元素上的所有注解.
该方法忽略继承的注解,如果没有注解直接存在此元素上,返回null.<T extends Annotation> T[] getDeclaredAnotationsByType(Class<T> annotationClass)
返回直接存在此元素上所有指定类型注解,
该方法忽略继承的注解Annotion[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解机器注解对应的重复注解容器.
该方法忽略继承注解,如果没有注释直接存在于此元素上,则返回长度为零的一个数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。
理解注解与反射接口后,我们通过自定义注解来把知识点融合应用一下
- 定义注解
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyMethodAnnotation {
public String title() default "";
public String description() default "";
}- 使用注解
public class TestMethodAnnotation {
@Override @MyMethodAnnotation(title = "toStringMethod", description = "override toString method") public String toString() { return "Override toString method"; }
@Deprecated @MyMethodAnnotation(title = "old static method", description = "deprecated old static method") public static void oldMethod() { System.out.println("old method, don't use it."); }
@SuppressWarnings({"unchecked", "deprecation"}) @MyMethodAnnotation(title = "test method", description = "suppress warning static method") public static void genericsTest() throws FileNotFoundException { List l = new ArrayList(); l.add("abc"); oldMethod(); }}- 用反射接口获取注解信息
public static void main(String[] args) { try { // 获取所有methods Method[] methods = TestMethodAnnotation.class.getClassLoader() .loadClass(("com.pdai.java.annotation.TestMethodAnnotation")) .getMethods();
// 遍历 for (Method method : methods) { // 方法上是否有MyMethodAnnotation注解 if (method.isAnnotationPresent(MyMethodAnnotation.class)) { try { // 获取并遍历方法上的所有注解 for (Annotation anno : method.getDeclaredAnnotations()) { System.out.println("Annotation in Method '" + method + "' : " + anno); }
// 获取MyMethodAnnotation对象信息 MyMethodAnnotation methodAnno = method .getAnnotation(MyMethodAnnotation.class);
System.out.println(methodAnno.title());
} catch (Throwable ex) { ex.printStackTrace(); } } } } catch (SecurityException | ClassNotFoundException e) { e.printStackTrace(); }}- 测试的输出
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @java.lang.Deprecated()Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @com.pdai.java.annotation.MyMethodAnnotation(title=old static method, description=deprecated old static method)old static methodAnnotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.genericsTest() throws java.io.FileNotFoundException' : @com.pdai.java.annotation.MyMethodAnnotation(title=test method, description=suppress warning static method)test methodAnnotation in Method 'public java.lang.String com.pdai.java.annotation.TestMethodAnnotation.toString()' : @com.pdai.java.annotation.MyMethodAnnotation(title=toStringMethod, description=override toString method)toStringMethod注解的本质(未完结)
Section titled “注解的本质(未完结)”这部分要扯到动态代理和注解的处理器,emmm比较麻烦.
后面发完动态处理后返回来填坑