Java基础之注解机制 - 详解

        注解是JDK1.5 版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它是框架学习和设计者必须掌握的基础。

1、注解基础

注解的主要作用:

  1. 生成文档,通过代码里标识的元数据生成javadoc文档。
  2. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
  3. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
  4. 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。

Java注解分类:

  • 标准注解(Java自带),包括@Override、@Deprecated和@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。
  • 元注解,元注解是用于定义注解的注解,包括@Retention、@Target@Inherited、@Documented,@Retention用于标明注解被保留的阶段,@Target用于标明注解使用的范围,@Inherited用于标明注解可继承,@Documented用于标明是否生成javadoc文档。
  • 自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。

 2、标准注解

package cn.boo.test;

/**
 * @author byh
 */
public class Service implements IService{
    @Override // 重写时
    public void Test() {
        System.out.println("测试重写");
    }
    @Deprecated // 废弃方法
    public void delete(){
        System.out.println("废弃方法");
    }

    public static void main(String[] args) {
        new Service().delete();
    }
}
Java 1.5 开始自带的标准注解,包括 @Override 、@Deprecated和 @SuppressWarnings
  • @Override:表示当前的方法定义将覆盖父类中的方法
  • @Deprecated:表示代码被弃用,如果使用了被@Deprecated注解的代码则编译器将发出警告
  • @SuppressWarnings:表示关闭编译器警告信息
我们再具体看下这几个内置注解,同时通过这几个内置注解中的元注解的定义来引出元注解。

@Override

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}
从它的定义我们可以看到,这个注解可以被用来修饰方法,并且它只在编译时有效,在编译后的class 文件中便不再存在。这个注解的作用我们大家都不陌生,那就是告诉编译器被修饰的方法是重写的父类的中的相同签名的方法,编译器会对此做出检查,若发现父类中不存在这个方法或是存在的方法签名不同,则会报错。
@Target与@Retention为元注解,下方会讲到。

@SuppressWarnings

@Target({TYPE, FIELD, METHOD, PARAMETER,CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[] 。它的作用是告诉编译器忽略指定的警告信息,它可以取的值如下所示:

 

3、Java元注解

3.1、@Target

Target 注解的作用是:描述注解的使用范围(即:被修饰的注解可以用在什么地方) 。
Target 注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages types (类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch 参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType枚举中。

 

public enum ElementType {
    TYPE, // 类、接口、枚举类
    FIELD, // 成员变量(包括:枚举常量)
    METHOD, // 成员方法
    PARAMETER, // 方法参数
    CONSTRUCTOR,  // 构造方法
    LOCAL_VARIABLE, // 局部变量
    ANNOTATION_TYPE, // 注解类
    PACKAGE, // 可用于修饰:包

    /**
     * Type parameter declaration
     *
     * @since 1.8 // 新增版本
     */
    TYPE_PARAMETER, // 类型参数

    /**
     * Use of a type
     *
     * @since 1.8 // 新增版本
     */
    TYPE_USE // 使用类型的任何地方
}

3.2、@Retention & @RetentionTarget

Reteniton 注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。
Reteniton 注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。

 

public enum RetentionPolicy {
    SOURCE, // 源文件保留
    CLASS, // 编译期保留,默认值
    RUNTIME // 运行期保留,可通过反射去获取注解信息
}

3.3、@Documented

Documented 注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
使用 Javadoc 工具可以生成 @TestDocAnnotation注解信息

 

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface TestDocAnnotation {
    public String value() default "default";
}
@TestDocAnnotation("myMethodDoc")
public void testDoc() {
}

3.4、@Inherited

Inherited 注解的作用:被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation ,则其子类将自动具有该注解。
  1. 定义@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());
        }
    }
}

 3.5、@Native (Java8)

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native注解不常使用

 3.6、注解与反射接口

定义注解后,如何获取注解中的内容呢?反射包java.lang.reé ect下的AnnotatedElement接口提供这些方法。这里注意:只有注解被定义为RUNTIME后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement 接口是所有程序元素(ClassMethod和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。

boolean isAnnotationPresent(Class<?extendsAnnotation> annotationClass)
判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回 false 。注意:此方法会忽略注解对应的注解容器。

 <T extends Annotation> TgetAnnotation(Class<T> annotationClass)

返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null

 Annotation[] getAnnotations()

 返回该程序元素上存在的所有注解,若没有注解,返回长度为0的数组。

<T extends Annotation> T[]getAnnotationsByType(Class<T>annotationClass)
返回该程序元素上存在的、指定类型的注解数组。没有注解对应类型的注解时,返回长度为0 的数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。getAnnotationsByType 方法与getAnnotation的区别在于, getAnnotationsByType 会检测注解对应的重复注解容器。若程序元素为类,当前类上找不到注解,且该注解为可继承的,则会去父类上检测对应的注解。

 <T extends Annotation> TgetDeclaredAnnotation(Class<T> annotationClass)

返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释。如果没有注释直接存在于此元素上,则返回null

 <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)

返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释

 Annotation[] getDeclaredAnnotations()

返回直接存在于此元素上的所有注解及注解对应的重复注解容器。与此接口中的其他方法不同,该方法将忽略继承的注解。如果没有注释直接存在于此元素上,则返回长度为零的一个数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。

 

4、自定义注解

当我们理解了解了标准注解与元注解之后,就可以进行撰写自定义注解了。

  • 定义注解
package cn.boo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author byh
 * 自定义注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnnotation {
    public String title() default "";//默认为""
    public String description() default "";//默认为""
}
  • 使用定义的注解
package cn.boo.annotation;
import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
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();
    }
}
  • 用反射接口获取注解信息

main方法定义在使用注解的类中进行测试

public static void main(String[] args) {
        try {
            // 获取所有methods
            Method[] methods = TestMethodAnnotation.class.getClassLoader().loadClass(("cn.boo.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();
        }
    }

返回信息如下代表成功

Connected to the target VM, address: '127.0.0.1:29109', transport: 'socket'
Annotation in Method 'public java.lang.String cn.boo.annotation.TestMethodAnnotation.toString()' : @cn.boo.annotation.MyMethodAnnotation(title=toStringMethod, description=override toString method)
toStringMethod
Annotation in Method 'public static void cn.boo.annotation.TestMethodAnnotation.oldMethod()' : @java.lang.Deprecated()
Annotation in Method 'public static void cn.boo.annotation.TestMethodAnnotation.oldMethod()' : @cn.boo.annotation.MyMethodAnnotation(title=old static method, description=deprecated old static method)
old static method
Annotation in Method 'public static void cn.boo.annotation.TestMethodAnnotation.genericsTest() throws java.io.FileNotFoundException' : @cn.boo.annotation.MyMethodAnnotation(title=test method, description=suppress warning static method)
test method
Disconnected from the target VM, address: '127.0.0.1:29109', transport: 'socket'

Process finished with exit code 0

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值