第八篇:注解,So easy

一、前言

二、注解地位

注解(Annotation),也叫元数据(Metadata),是Java5的新特性,JDK5引入了Metadata很容易的就能够调用Annotations。注解与类、接口、枚举在同一个层次,并可以应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中,用来对这些元素进行说明注释。

三、注解定义

(1)定义:以@interface关键字定义;

(2)成员:成员以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型;

(3)四个元注解:常用的两个 @Retention 和 @Target 注解需要标明注解的生命周期,注解的修饰目标等信息,这些信息是通过元注解实现;

(4)使用时,被注解标注的成员(类成员+属性成员)赋值是通过@Annotation(name=value)的形式。

以 java.lang.annotation 中定义的 Target 注解来说明:

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.ANNOTATION_TYPE } )
public @interface Target {
    ElementType[] value();
}

源码分析如下:

第一:元注解@Retention,成员value的值为RetentionPolicy.RUNTIME;

第二:元注解@Target,成员value是个数组,用{}形式赋值,值为ElementType.ANNOTATION_TYPE;

第三:成员名称为value,类型为ElementType[]。

另外,需要注意一下,如果成员名称是value,在赋值过程中可以简写。如果成员类型为数组,但是只赋值一个元素,则也可以简写。如上面的简写形式为:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

四、注解的分类

注解的分类有两种分法:

第一种分法:内置注解、元注解、自定义注解;

第二种分法:生命周期:SOURCE、CLASS、RUNTIME。

4.1 第一种分法:内置注解、元注解、自定义注解

1、基本内置注解,是指Java自带的几个Annotation,如@Override重写、@Deprecated废弃、@SuppressWarnings编译器忽略注解;

2、元注解(meta-annotation),是指负责注解其他注解的注解,JDK 1.5及以后版本定义了4个标准的元注解类型,如下:

@Target

@Retention 生命周期 Source Class Runtime

@Documented

@Inherited

3、自定义注解,根据需要可以自定义注解,自定义注解需要用到上面的meta-annotation。

4.2 第二种分法:按生命周期划分:SOURCE、CLASS、RUNTIME

注解需要标明注解的生命周期,生命周期是通过元注解 @Retention 实现,注解的值是 enum 类型的 RetentionPolicy,包括以下几种情况:

public enum RetentionPolicy {
    /**
     * 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃.
     * 这意味着:Annotation仅存在于编译器处理期间,编译器处理完之后,该Annotation就没用了
     */
    SOURCE,
 
    /**
     * 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期.
     */
    CLASS,
 
    /**
     * 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,
     * 保存到class对象中,可以通过反射来获取
     */
    RUNTIME
}

五、四个元注解:@Documented、@Inherited、@Retention、@Target

5.1 @Documented 指定注解是否作为公用api

@Documented:标记注解,用于描述其它类型的注解应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Documented 标记注解,指定注解(内置注解或自定义注解)是否作为公用API。

5.2 @Inherited 指定注解是否允许被子类继承

@Inherited:标记注解,允许子类继承父类的注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Inherited 作为元注解,指定注解(内置注解或自定义注解)是否允许被子类继承。

5.3 @Retention 生命周期 Source Class Runtime

@Retention:指Annotation被保留的时间长短,标明注解的生命周期,3种RetentionPolicy取值含义上面以说明。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

@Retention 作为元注解,指定注解(内置注解或自定义注解)的声明周期,值为RetentionPolicy中枚举值,三个SOURCE、CLASS、RUNTIME。

5.4 @Target 指定注解修饰的目标 8个

@Target:标明注解的修饰目标,共有八种。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
// ElementType取值
public enum ElementType {
    /** 包 */
    PACKAGE
    /** 类型:类、接口(包括注解类型)或枚举 */
    TYPE,
    /** field属性,也包括enum常量使用的注解 */
    FIELD,
    /** 方法 */
    METHOD,
    /** 方法参数 */
    PARAMETER,  
    /** 构造函数 */
    CONSTRUCTOR,
    /** 局部变量 */
    LOCAL_VARIABLE,
    /** 注解上使用的元注解 */
    ANNOTATION_TYPE,
}

@Target 作为元注解,指定注解(内置注解或自定义注解)的作用目标,值为ElementType中的枚举值,值为8个,常见的TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、CONSTRUCTOR、PACKAGE、ANNOTATION_TYPE。

金手指:仅此8种,返回值上面是不可以使用注解的。

六、自定义注解

6.1 注解生命周期(Source Class Runtime)

前面有提到注解按生命周期来划分可分为3类:

1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;

2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;

3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。

那怎么来选择合适的注解生命周期呢?

首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。

如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;

如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;

如果只是做一些检查性的操作,比如 @Override和 @SuppressWarnings,则可选用 SOURCE 注解。

下面来介绍下运行时注解的简单运用。

6.2 注解的使用:获取注解

你需要通过反射来获取运行时注解,可以从 Package、Class、Field、Method…上面获取,基本方法都一样,几个常见的方法如下:

/** 
 * 获取指定类型的注解 
 */  
public <A extends Annotation> A getAnnotation(Class<A> annotationType);  
  
/** 
 * 获取所有注解,如果有的话 
 */  
public Annotation[] getAnnotations();  
  
/** 
 * 获取所有注解,忽略继承的注解 
 */  
public Annotation[] getDeclaredAnnotations();  
  
/** 
 * 指定注解是否存在该元素上,如果有则返回true,否则false 
 */  
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType);  
  
/** 
 * 获取Method中参数的所有注解 
 */  
public Annotation[][] getParameterAnnotations(); 
要使用这些函数必须先通过反射获取到对应的元素:Class、Field、Method 等。
自定义注解来看下自定义注解的简单使用方式,这里先定义3个运行时注解:

// 适用类、接口(包括注解类型)或枚举  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE)  
public @interface ClassInfo {  
    String value();  
}  

// 适用field属性,也包括enum常量  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)  
public @interface FieldInfo {  
    int[] value();  
}  

// 适用方法  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
public @interface MethodInfo {  
    String name() default "long";  
    String data();  
    int age() default 27;  
}

这3个注解分别适用于不同的元素,并都带有不同的属性,在使用注解是需要设置这些属性值。

再定义一个测试类来使用这些注解:

/** 
 * 测试运行时注解 
 */  
@ClassInfo("Test Class")  
public class TestRuntimeAnnotation {  
  
    @FieldInfo(value = {1, 2})  
    public String fieldInfo = "FiledInfo";  
  
    @FieldInfo(value = {10086})  
    public int i = 100;  
  
    @MethodInfo(name = "BlueBird", data = "Big")  
    public static String getMethodInfo() {  
        return TestRuntimeAnnotation.class.getSimpleName();  
    }  
}  

使用还是很简单的,最后来看怎么在代码中获取注解信息:

/** 
 * 测试运行时注解 
 */  
private void _testRuntimeAnnotation() {  
    StringBuffer sb = new StringBuffer();  
    Class<?> cls = TestRuntimeAnnotation.class;  
    Constructor<?>[] constructors = cls.getConstructors();  
    // 获取指定类型的注解  
    sb.append("Class注解:").append("\n");  
    ClassInfo classInfo = cls.getAnnotation(ClassInfo.class);  
    if (classInfo != null) {  
        sb.append(Modifier.toString(cls.getModifiers())).append(" ")  
                .append(cls.getSimpleName()).append("\n");  
        sb.append("注解值: ").append(classInfo.value()).append("\n\n");  
    }  
  
    sb.append("Field注解:").append("\n");  
    Field[] fields = cls.getDeclaredFields();  
    for (Field field : fields) {  
        FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);  
        if (fieldInfo != null) {  
            sb.append(Modifier.toString(field.getModifiers())).append(" ")  
                    .append(field.getType().getSimpleName()).append(" ")  
                    .append(field.getName()).append("\n");  
            sb.append("注解值: ").append(Arrays.toString(fieldInfo.value())).append("\n\n");  
        }  
    }  
  
    sb.append("Method注解:").append("\n");  
    Method[] methods = cls.getDeclaredMethods();  
    for (Method method : methods) {  
        MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);  
        if (methodInfo != null) {  
            sb.append(Modifier.toString(method.getModifiers())).append(" ")  
                    .append(method.getReturnType().getSimpleName()).append(" ")  
                    .append(method.getName()).append("\n");  
            sb.append("注解值: ").append("\n");  
            sb.append("name: ").append(methodInfo.name()).append("\n");  
            sb.append("data: ").append(methodInfo.data()).append("\n");  
            sb.append("age: ").append(methodInfo.age()).append("\n");  
        }  
    }  
  
    System.out.print(sb.toString());  
}  

七、面试金手指

7.1 注解概要

7.1.1 注解概要

(1)定义:以@interface关键字定义;

(2)成员:成员以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型;

(3)四个元注解:常用的两个 @Retention 和 @Target 注解需要标明注解的生命周期,注解的修饰目标等信息,这些信息是通过元注解实现;

(4)使用时,被注解标注的成员(类成员+属性成员)赋值是通过@Annotation(name=value)的形式。

7.1.2 四个元注解

四个元注解:

@Documented 指定注解是否作为公用api;

@Inherited 指定注解是否允许被子类继承;

@Retention 生命周期 Source Class Runtime;

@Target 指定注解修饰的目标 8个:指定注解标注目标 8个 包、类型(类、接口、枚举,三者地位相等)、域、方法参数、普通方法、构造函数、局部变量、注解上使用的元注解。

7.1.3 @Retention元注解

对于 @Retention 元注解,该元注解是用来标注生命周期的,注解的值是 enum 类型的 RetentionPolicy,一共包括三种情况:SOURCE、CLASS、RUNTIME。

SOURCE:注解只保留在源文件java文件中,当Java文件编译成class文件的时候,注解被遗弃.注解Annotation仅存在于编译器处理期间,编译器处理完之后,该Annotation就没用了;

CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;

RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,保存到class对象中,可以通过反射来获取。

7.2 注解分类方式

7.2.1 第一种分法:内置注解、元注解、自定义注解

第一种分法:内置注解、元注解、自定义注解

1、基本内置注解,是指Java自带的几个Annotation,如@Override重写、@Deprecated废弃、@SuppressWarnings编译器忽略注解;

2、四个元注解(meta-annotation),是指负责注解其他注解的注解,JDK 1.5及以后版本定义了4个标准的元注解类型,如下:

@Target 指定注解标注目标 8个 包、类型(类、接口、枚举)、域、方法、方法参数、构造函数、局部变量、注解上使用的元注解

@Retention 生命周期 Source Class Runtime

@Documented

@Inherited

3、自定义注解,根据需要可以自定义注解,自定义注解需要用到上面的meta-annotation。

7.2.2 第二种分法:按生命周期划分

略,见上文。

八、尾声

注解,So easy,完成了。

天天打码,天天进步!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祖母绿宝石

打赏一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值