Java注解解析及其使用

本文详细介绍了Java注解的概念,如何用于编写文档、代码分析和编译检查,以及常用的基本注解如@Override、@Deprecated和@FunctionalInterface的用法。深入探讨了元注解和自定义注解,并通过实例展示了注解的解析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是注解

官网上是这么介绍注解的: Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。

其实说白了,注解只不过是一种特殊的注释而已

作用分类

  1. 编写文档: 通过代码里标识的元数据(注解)生成文档(比如doc文档)
  2. 代码分析:通过代码里标识的元数据(注解)对代码进行分析(反射)
  3. 编译检查: 通过代码里标识的元数据(注解)让编译器能够实现基本的编译检查(例如:Override)

Java提供的基本注解

@Override(限定父类重写方法)

@Override: 当子类重写父类方法时,子类可以加上这个注解,那这有什么什么用?这可以确保子类确实重写了父类的方法,避免出现低级错误。

@Deprecated(标示已过时)

@Deprecated:这个注解用于表示某个程序元素类,方法等已过时,当其他程序使用已过时的类,方法时编译器会给出警告。

@SuppressWarnings(抑制编译器警告)

被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告,例如修饰一个类,那他的字段,方法都是显示警告。

示例:

// 抑制编译器对该类以及类中方法的所有警告
@SuppressWarnings("all")
public class test(){
    // 抑制编译器对该方法的所有警告
    @SuppressWarnings("all")
    public static void test1(){}
}
@SafeVarargs(抑制编译器unchecked警告)

在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告。鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全的操作,可使用@SafeVarargs进行标记,这样的话,Java编译器就不会报unchecked警告。

@FunctionalInterface(函数式接口)

这个注解保证这个接口只有一个抽象方法,FunctionalInterface只能修饰接口。

元注解

元注解其实就是描述注解的注解。

@Retention

Reteniton注解的作用是:描述注解保留的时间范围。

Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。

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

Target注解的作用是:描述注解的使用范围

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

public enum ElementType {
    TYPE, // 类、接口、枚举类
    FIELD, // 成员变量(包括:枚举常量)
    METHOD, // 成员方法
    PARAMETER, // 方法参数
    CONSTRUCTOR, // 构造方法
    LOCAL_VARIABLE, // 局部变量
    ANNOTATION_TYPE, // 注解类
    PACKAGE, // 可用于修饰:包
    TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
    TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}
@Documented

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

@Inherited

Inherited注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。

类型注解

在以前的 Java 版本中,开发者只能将注解(Annotation)写在声明中。对于 Java 8,注解可以写在使用类型的任何地方,例如声明、泛型和强制类型转换等语句,例如:

@Encrypted String data;
List<@NonNull String> strings;
myGraph = (@Immutable Graph) tmpGraph;

自定义注解

自定义注解语法
public @interface annotationName{
    
}
注解中的属性

既然注解本质是一个接口,那么注解也拥有接口的特性,例如定义常量与方法,且在定义抽象方法时,其返回值须为以下几种:

  1. 基本数据类型
  2. String
  3. 枚举
  4. 注解
  5. 以上类型的数组

例:

public @interface test {
    final String  NAME = "zhangsan";
    String name() default="zhangsan";
    int age();
    ElementType elementType();
    SuppressWarnings suppressWarnings();
    String[] names();
}

注意:

  1. 在使用注解时,需要给属性赋值,如果在定义时,设定了默认值,则可以忽略赋值操作,采取默认值。

    @test(age=12 ...)
    
  2. 如果在定义属性时,只有一个属性,且名字叫value,则可以省略不写,直接定义值即可,如果有多个,则不可省略

    例:

    // 定义
    public @interface test {
        int value();
    }
    // 使用
    @test(12)
    

属性赋值示例:

@test(name = "zhangsan", //字符串
      age = 12, // 数值
      elementType= ElementType.TYPE, // 枚举
      suppressWarnings = @SuppressWarnings({}), // 注解
      names={"zhangsan","lisi"}) //数组,采用大括号分割,如果数组中只有一个值,大括号可省略不写

注解的本质

我们自定义一个名为annotationName的注解,生成class文件之后,反编译该文件:

public interface annotationName extends java.lang.annotation.Annotation {
}

从上面我们可以看到,注解其实就是一个默认继承了java.lang.annotation.Annotation接口的一个接口。

注解的解析

定义三个注解
// 允许作用在类上
@Target({ElementType.TYPE})
// 运行期保留,可通过反射去获取注解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAnnotation {
    String className();
}
// 允许作用在方法上
@Target({ElementType.METHOD})
// 运行期保留,可通过反射去获取注解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnnotation {
    String methodName();
}
// 允许作用在成员变量上
@Target({ElementType.FIELD})
// 运行期保留,可通过反射去获取注解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface AttributesAnnotation {
    String attributesName();
}
定义类并使用注解
@ClassAnnotation(className = "com.minio.demo.Test")
public class Test {

    @AttributesAnnotation(attributesName = "zhangsan")
    public String name = "name";

    @MethodAnnotation(methodName = "show")
    public void show() {
        System.out.println("方法被执行...");
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        // 获取被注解类的class对象
        Class<Test> testClass = Test.class;
        // 获取注解对象 此操作会在内存中生成一个该注解接口的子类实现对象
        ClassAnnotation classAnnotation = testClass.getAnnotation(ClassAnnotation.class);
        // 调用注解对象中对应的抽象方法,得到其设定的值
        String className = classAnnotation.className();
        System.out.println(className);
        // 获取定义的className的Class对象
        Class<?> aClass = Class.forName(className);
        // 得到该类对象中的所有方法
        Method[] methods = aClass.getMethods();
        String methodName = "";
        String attributesName = "";
        for (Method method : methods) {
            // 判断该方法是否存在该注解
            // 采用判断isAnnotationPresent方法判断
            if (method.isAnnotationPresent(MethodAnnotation.class)) {
                // 如果存在,取出该注解
                MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
                // 获取注解信息
                methodName = methodAnnotation.methodName();
            }
        }
        for (int i = 0; i < methods.length; ++i) {
            // 得到类方法中的注解
            MethodAnnotation methodAnnotation = methods[i].getAnnotation(MethodAnnotation.class);
            // 如果不等于null,则代表改方法存在注解
            // 采用if是否空判断法
            if (methodAnnotation != null) {
                // 获取注解信息
                methodName = methodAnnotation.methodName();
            }
        }
        // 执行对应的方法
        Method method = aClass.getMethod(methodName);
        // 创建该类对象
        Object obj = aClass.newInstance();
        // 执行方法
        method.invoke(obj);
        // 得到所有字段
        Field[] declaredFields = aClass.getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            AttributesAnnotation annotation = declaredFields[i].getAnnotation(AttributesAnnotation.class);
            attributesName = annotation.attributesName();
        }
        System.out.println(attributesName);
    }

}

输出结果:

com.minio.demo.Test
方法被执行...
zhangsan
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值