Java:注解(Annotation)

一. 基本概念

Java 注解(Annotation)是一种元数据形式,提供关于程序代码的补充信息,但不会直接影响代码的执行。注解可以用于类、方法、变量、参数等元素,主要用于编译时检查、运行时反射或生成文档等场景。

二. 注解的核心作用

注解的核心价值在于提供元数据,主要应用场景包括:

  1. 编译期检查:如 @Override 确保方法正确重写父类方法,编译器会校验合法性。
  2. 生成文档:配合 Javadoc 工具,从注解中提取信息生成文档(如 @Deprecated 标记过时 API)。
  3. 运行时处理:框架(如 Spring、MyBatis)通过反射读取注解,动态执行逻辑(如@Autowired实现依赖注入)。
  4. 代码生成:通过 APT(Annotation Processing Tool)在编译期根据注解自动生成代码(如 Lombok 的@Data生成 getter/setter)。

三. 注解的分类

根据用途和来源,注解可分为三类:

1. 内置注解(JDK 自带)

JDK 提供了几个基础注解,用于常见场景:

  • @Override:标记方法重写父类或接口的方法。编译器会校验方法签名是否正确,若未正确重写则报错。
class Parent {
    void doSomething() {}
}

class Child extends Parent {
    @Override  // 确保此方法正确重写父类
    void doSomething() {}
}
  • @Deprecated:标记类、方法或字段已过时。编译器会对使用过时元素的代码发出警告,文档中也会标记 “过时” 说明。
@Deprecated
public class OldClass {}  // 此类已过时
  • @SuppressWarnings:抑制编译器警告(如未使用变量、 unchecked 转换等)。需指定警告类型(如"unchecked"、“unused”)。
@SuppressWarnings("unused")  // 抑制"变量未使用"的警告
int unusedVar = 10;
  • @SafeVarargs(Java 7+):标记方法的可变参数是类型安全的,抑制 “堆污染” 警告。
  • @FunctionalInterface(Java 8+):标记接口为函数式接口(仅含一个抽象方法),编译器会校验合法性。

2. 元注解(修饰注解的注解)

元注解用于定义其他注解的行为(如注解可修饰的元素、保留周期等)。JDK 提供 4 个标准元注解:

元注解作用常用取值(ElementType)
@Target指定注解可应用的位置(如类、方法、字段等)TYPE(类)、METHOD(方法)、FIELD(字段)等
@Retention指定注解的保留阶段(源码、字节码、运行时)RUNTIME(运行时可通过反射获取,最常用)
@Documented标记注解是否会被javadoc工具提取到文档中-
@Inherited标记注解是否可被子类继承(仅对类注解有效)-

@Target:指定注解可应用的元素类型(如类、方法、字段等)。取值来自ElementType枚举,常见值:

  • TYPE:类、接口、枚举
  • METHOD:方法
  • FIELD:字段(成员变量)
  • PARAMETER:方法参数
  • CONSTRUCTOR:构造器
  • LOCAL_VARIABLE:局部变量
    示例:限制注解只能用于方法和字段
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MyAnnotation {}

@Retention:指定注解的保留周期(即注解信息在哪个阶段有效)。取值来自RetentionPolicy枚举:

  • SOURCE:仅在源码中保留,编译后丢弃(如@Override)。
  • CLASS:保留到编译期(.class 文件中),但运行时不加载到 JVM(默认值)。
  • RUNTIME:保留到运行时,可通过反射读取(如 Spring 的@Autowired)。
    示例:注解保留到运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

@Documented:标记注解会被 Javadoc 工具提取到文档中(默认注解不会出现在文档里)。
@Inherited:标记注解可被子类继承(仅对类级注解有效)。即父类使用该注解,子类若未单独标注,也会继承父类的注解。

3. 自定义注解

开发者可根据需求定义自己的注解,需使用 @interface 关键字,并结合元注解指定其行为。
自定义注解的语法:


// 元注解:指定注解的保留周期和可修饰的元素
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
    // 注解的"属性"(类似方法声明,可指定默认值)
    String value() default "操作日志";  // value是特殊属性,使用时可省略属性名
    boolean needLog() default true;
}

使用自定义注解:


public class UserService {
    // 使用@Log注解,可指定属性值(value可省略属性名)
    @Log("用户登录")
    public void login(String username) {
        // 业务逻辑
    }
}

四、注解的解析

注解需要通过工具(编译器、APT、反射)解析才能发挥作用。解析方式分为两类:

1. 编译期解析(APT)

通过 Annotation Processing Tool 在编译期扫描注解,生成代码或进行校验(如 Lombok、ButterKnife)。
原理:自定义处理器实现 javax.annotation.processing.Processor 接口,在编译时读取注解信息并处理。

2. 运行时解析(反射)

若注解的 @Retention 为 RUNTIME ,可通过反射 API 在运行时获取注解信息并执行逻辑。
示例:解析 @Log 注解并打印日志

public class LogProcessor {
    public static void process(Object obj) throws Exception {
        // 获取类中所有方法
        Method[] methods = obj.getClass().getMethods();
        for (Method method : methods) {
            // 判断方法是否有@Log注解
            if (method.isAnnotationPresent(Log.class)) {
                Log logAnnotation = method.getAnnotation(Log.class);
                // 读取注解属性
                String logInfo = logAnnotation.value();
                boolean needLog = logAnnotation.needLog();
                
                if (needLog) {
                    System.out.println("执行日志:" + logInfo);
                }
            }
        }
    }

    // 测试
    public static void main(String[] args) throws Exception {
        UserService service = new UserService();
        process(service);  // 输出:执行日志:用户登录
    }
}

五. 常见应用场景

  • 框架配置(如 Spring 的 @Autowired@Component)。
  • 测试工具标记(如 JUnit 的 @Test)。
  • 代码生成(如 Lombok 的 @Data)。
  • 数据校验(如 Hibernate Validator 的 @NotNull)。
  • 权限控制:自定义@RequiresPermission注解,拦截器解析并校验权限。

六.注意事项

  • 注解的成员变量只能是基本类型、String、Class、枚举、注解或它们的数组。
  • 默认值通过 default 关键字指定。
  • 运行时处理的注解需谨慎设计,避免性能开销。

通过合理使用注解,可以显著提升代码的可读性和可维护性,同时减少样板代码的编写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值