Java进阶——2. 注解

1 元注解

1.1 @Target

指定注解可应用的元素类型(类、方法、字段等)

1.2 @Retention

指定注解保留策略(SOURCE/CLASS/RUNTIME)

1.2.1 RetentionPolicy.SOURCE

源码保留,并被编译器忽略

1.2.1.1 APT

在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。
实现步骤:
1、创建annotation模块(Java 或 Kotlin Library模块),存放注解
2、创建processor模块(Java 或 Kotlin Library模块),实现注解处理器:继承AbstractProcessor类,重写process方法(如下)
3、在app模块中使用注解处理器:annotationProcessor project(‘:processor’)
4、注册注解处理器
  4.1、processor/src/main/resources/META-INF/services/目录下创建文件javax.annotation.processing.Processor,内容为处理器的全类名
  4.2、使用AutoService简化

AbstractProcessor(注解处理器)
注解处理器执行多次(通常两次)是 Java 编译器多轮处理机制的正常表现
1、第一轮:处理原始代码中的注解
  第一次调用process()时,处理器会扫描原始源代码(开发者编写的代码)中的注解,执行核心逻辑(如解析注解、生成代码)。
2、第二轮:处理生成代码中的注解(或收尾)
  如果第一轮生成了新的 Java 代码,编译器会将这些代码视为 “新的源代码”,触发第二轮处理:
  # 若生成的代码中包含新的注解(如@Generated或自定义注解),第二轮会处理这些新注解。
  # 若生成的代码中没有注解,第二轮的RoundEnvironment会是空的,但处理器仍会被调用一次,此时RoundEnvironment.processingOver()返回true(表示处理即将结束),处理器可在此轮做收尾工作(如释放资源)。

如何处理多轮调用:
1、在process()中先判断是否有需要处理的注解

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 若本轮无注解且处理即将结束,直接返回
        if (roundEnvironment.processingOver() || set.isEmpty()) {
            return false;
        }

        // 处理注解的核心逻辑(仅在有注解时执行)
        processAnnotation(roundEnvironment);
        return true;
    }

2、记录已处理的元素,避免重复处理

    private Set<Element> processedElements = new HashSet<>();

    private void processAnnotation(RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElenentsAnnotatedWith(MyAnnotation.class);
        for (Element elenent : elements) {
            if (processedElements.contains(element)(
                continue; //跳过已处理的元素
            }
            processedElements.add(element);
            //生成代码...
        }
    }
1.2.1.2 @IntDef

AndroidX 注解库提供的一个元注解,主要作用是实现整数常量的类型安全,替代枚举(Enum)以优化性能,尤其在 Android 开发中被广泛使用。

背景:
枚举(Enum):类型安全,但每个枚举实例都是一个对象,占用更多内存和性能(尤其在 Android 这种资源受限的环境中)。
整数常量(public static final int):轻量,但缺乏类型检查,调用方法时可能传入无效的整数值,导致逻辑错误。

用法:

public class Status {
    //1.定义整数常量
    public static final int IDLE = 0;
    public static final int RUNNING = 1;
    public static final int PAUSED = 2;
    public static final int STOPPED = 3;
    
    //2.用@IntDef标记允许的常量,并用@Retention指定保留策略
    @IntDef({IDLE,RUNNING,PAUSED,STOPPED}) // 限定只能使用这几个常量
    @Retention(RetentionPolicy.SOURCE) // 仅在源码中保留(编译时检查)
    public @interface Validstatus {} // 自定义注解,用于后续标记变量/参数
}
public class Permission {
    //定义标志位常量(通常是2的幂,方便位运算)
    public static final int READ = 1 << 0; // 1
    public static final int WRITE = 1 << 1; // 2
    public static final int DELETE = 1 << 2;// 4
    
    //添加flag = true,表示允许常量组合(通过|运算)
    @IntDef(flag = true, value = {READ, WRITE, DELETE})
    @Retention(RetentionPolicy.SoURCE)
    public @interface Permissions {}
}

//使用:允许组合多个常量
public void requestPermission(@Permission.Permissions int permission) {
    // 例如:同时请求读和写权限
    requestPermission(Permission.READ | Permission.WRITE);
}

1.2.2 RetentionPolicy.CLASS

字节码保留,但JVM会忽略

1.2.2.1 字节码增强技术

在 Java 字节码(.class 文件)生成后、加载前或运行时动态修改字节码的技术

场景:
AOP(面向切面编程):在不修改业务代码的情况下,为多个类的方法统一添加日志、事务、权限校验等逻辑
性能监控:自动为方法添加执行时间统计、调用次数记录等(如 Android 中的性能监控工具)–
热修复:动态替换有问题的方法字节码,修复线上 bug(如早期 Android 热修复方案)
代码生成优化:在编译期动态生成辅助代码,减少手动编写的模板代码(如 Dagger、ButterKnife 的部分逻辑)
兼容性适配:为不同版本的类添加适配逻辑,避免修改源代码

工具:
ASM:底层工具,直接操作字节码指令,性能极高,学习成本高,适用于性能敏感场景,如Android
Javassist:高层 API,支持用类似 Java 的语法修改字节码,无需了解字节码指令,性能中等,适合快速开发、逻辑简单的场景

1.2.3 RetentionPolicy.RUNTIME

由JVM保留,运行时会使用
与反射技术相结合,从而完成不同的逻辑判定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值