JDK 常用的原生注解详解

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

作为Java后端开发程序员,掌握JDK原生注解是基础且重要的技能。以下按照功能和类型分类,详细介绍必须掌握的JDK原生注解。

一、编译器提示类注解

这类注解主要用于给编译器提供额外信息,帮助开发者避免常见错误。

1. @Override

作用:标记方法是重写父类或实现接口的方法,如果方法签名不匹配,编译器会报错。

// 定义父类
class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

// 子类重写父类方法
class Dog extends Animal {
    // 使用 @Override 注解确保正确重写
    @Override
    public void makeSound() {
        System.out.println("汪汪汪!");
    }
    
    // 如果错误地写成makeSound(String sound),编译器会报错
    // @Override
    // public void makeSound(String sound) { // 编译错误:没有重写父类方法
    //     System.out.println(sound);
    // }
}

2. @Deprecated

作用:标记类、方法或字段已过时,不推荐使用。使用时编译器会给出警告。

public class OldUtility {
    // 标记为过时的方法
    @Deprecated
    public static void oldMethod() {
        System.out.println("这是过时的方法,请使用newMethod()");
    }
    
    // 推荐使用的新方法
    public static void newMethod() {
        System.out.println("这是推荐使用的新方法");
    }
    
    // 也可以为过时的元素提供替代方案说明
    @Deprecated(since = "1.8", forRemoval = true)
    public static void legacyMethod() {
        System.out.println("这个方法将在未来版本中移除");
    }
}

3. @SuppressWarnings

作用:抑制编译器警告,避免不必要的警告信息干扰开发。

import java.util.*;

public class SuppressWarningsExample {
    public static void main(String[] args) {
        // 抑制"rawtypes"警告(使用原始类型)
        @SuppressWarnings("rawtypes")
        List list = new ArrayList(); // 不指定泛型类型
        
        // 抑制"unchecked"警告(类型转换不安全)
        @SuppressWarnings("unchecked")
        List<String> stringList = (List<String>) getList();
        
        // 抑制多个警告类型
        @SuppressWarnings({"unchecked", "rawtypes"})
        Map map = new HashMap();
        
        System.out.println("警告已被抑制");
    }
    
    private static List getList() {
        return Arrays.asList("a", "b", "c");
    }
}

二、元注解(用于定义其他注解的注解)

元注解是用来注解其他注解的注解,是自定义注解的基础。

1. @Target

作用:指定注解可以应用的程序元素类型。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

// 定义一个只能用于方法的注解
@Target(ElementType.METHOD)
@interface MethodOnlyAnnotation {
    String value() default "";
}

// 定义一个可以用于类和方法的注解
@Target({ElementType.TYPE, ElementType.METHOD})
@interface ClassAndMethodAnnotation {
    String description() default "";
}

// 使用示例
@ClassAndMethodAnnotation(description = "这是一个测试类")
class TestClass {
    
    @MethodOnlyAnnotation("测试方法")
    @ClassAndMethodAnnotation(description = "测试方法描述")
    public void testMethod() {
        // 方法实现
    }
    
    // 以下代码会编译错误,因为 @MethodOnlyAnnotation 不能用于字段
    // @MethodOnlyAnnotation
    // private String field; // 编译错误
}

2. @Retention

作用:指定注解的保留策略(源码、编译时、运行时)。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 只在源码中保留,编译后丢弃
@Retention(RetentionPolicy.SOURCE)
@interface SourceOnlyAnnotation {
    String value() default "";
}

// 编译时保留,包含在class文件中,但运行时不可见
@Retention(RetentionPolicy.CLASS)
@interface ClassRetentionAnnotation {
    String value() default "";
}

// 运行时保留,可以通过反射获取
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeAnnotation {
    String name() default "";
    int version() default 1;
}

// 运行时注解使用示例
@RuntimeAnnotation(name = "UserService", version = 2)
class UserService {
    @RuntimeAnnotation(name = "saveUser", version = 1)
    public void saveUser(String username) {
        System.out.println("保存用户: " + username);
    }
}

3. @Documented

作用:指定注解是否包含在JavaDoc文档中。

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 这个注解会被包含在JavaDoc中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface PublicAPI {
    String value() default "这是一个公共API";
}

// 这个注解不会被包含在JavaDoc中(默认行为)
@Retention(RetentionPolicy.RUNTIME)
@interface InternalAPI {
    String value() default "内部API,不对外公开";
}

// 使用示例
@PublicAPI("用户服务公共接口")
class DocumentedExample {
    @PublicAPI("获取用户信息")
    public String getUserInfo() {
        return "用户信息";
    }
    
    @InternalAPI("内部处理逻辑")
    private void internalProcess() {
        // 内部处理
    }
}

4. @Inherited

作用:指定注解是否可以被子类继承。

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 使用 @Inherited,子类会继承父类的这个注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface InheritedAnnotation {
    String value() default "";
}

// 不使用 @Inherited,子类不会继承父类的这个注解
@Retention(RetentionPolicy.RUNTIME)
@interface NonInheritedAnnotation {
    String value() default "";
}

@InheritedAnnotation("父类注解")
@NonInheritedAnnotation("父类非继承注解")
class ParentClass {
    // 父类实现
}

class ChildClass extends ParentClass {
    // 子类实现
    // ChildClass会自动拥有@InheritedAnnotation注解
    // 但不会拥有@NonInheritedAnnotation注解
}

// 测试继承效果
class InheritanceTest {
    public static void main(String[] args) {
        // 检查子类是否继承了注解
        if (ChildClass.class.isAnnotationPresent(InheritedAnnotation.class)) {
            System.out.println("子类继承了@InheritedAnnotation");
        }
        
        if (ChildClass.class.isAnnotationPresent(NonInheritedAnnotation.class)) {
            System.out.println("子类继承了@NonInheritedAnnotation");
        } else {
            System.out.println("子类没有继承@NonInheritedAnnotation");
        }
    }
}

5. @Repeatable (Java 8+)

作用:允许在同一位置重复使用同一个注解。

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 定义重复注解的容器
@Retention(RetentionPolicy.RUNTIME)
@interface Authorities {
    Authority[] value();
}

// 定义可重复的注解
@Repeatable(Authorities.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Authority {
    String role();
}

// 使用重复注解
@Authority(role = "ADMIN")
@Authority(role = "USER")
@Authority(role = "GUEST")
class MultiRoleService {
    public void accessResource() {
        System.out.println("多角色访问控制");
    }
}

// 测试重复注解
class RepeatableTest {
    public static void main(String[] args) {
        Authority[] authorities = MultiRoleService.class.getAnnotationsByType(Authority.class);
        System.out.println("角色数量: " + authorities.length);
        for (Authority auth : authorities) {
            System.out.println("角色: " + auth.role());
        }
    }
}

三、函数式接口相关注解

@FunctionalInterface (Java 8+)

作用:标记接口为函数式接口(只包含一个抽象方法的接口),编译器会验证接口是否符合函数式接口的要求。

@FunctionalInterface
interface Calculator {
    // 函数式接口只能有一个抽象方法
    int calculate(int a, int b);
    
    // 可以包含默认方法
    default void printResult(int result) {
        System.out.println("计算结果: " + result);
    }
    
    // 可以包含静态方法
    static void showInfo() {
        System.out.println("这是一个计算器接口");
    }
}

// 错误示例:以下代码会编译错误,因为有两个抽象方法
// @FunctionalInterface
// interface InvalidFunctionalInterface {
//     void method1();
//     void method2(); // 编译错误:函数式接口只能有一个抽象方法
// }

// 使用函数式接口
class FunctionalInterfaceExample {
    public static void main(String[] args) {
        // 使用Lambda表达式实现函数式接口
        Calculator add = (a, b) -> a + b;
        Calculator multiply = (a, b) -> a * b;
        
        int result1 = add.calculate(10, 20);
        add.printResult(result1);
        
        int result2 = multiply.calculate(5, 6);
        multiply.printResult(result2);
    }
}

四、空值检查相关注解 (Java 8+)

这些注解主要用于静态分析工具,帮助检测潜在的空指针异常。

1. @Nullable

2. @NonNull

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

// 注意:严格来说,@Nullable和@NonNull不是JDK原生注解,
// 而是来自Checker Framework,但Java 8+的Optional类提供了类似功能

import java.util.Optional;

class NullSafetyExample {
    // 使用Optional来避免空值问题(JDK原生方式)
    public Optional<String> findUserName(int userId) {
        // 模拟查找用户,可能找不到
        if (userId == 1) {
            return Optional.of("张三");
        } else {
            return Optional.empty(); // 返回空的Optional,而不是null
        }
    }
    
    // 安全地使用Optional
    public void processUser(int userId) {
        Optional<String> userName = findUserName(userId);
        
        // 方式1:检查是否存在
        if (userName.isPresent()) {
            System.out.println("找到用户: " + userName.get());
        } else {
            System.out.println("用户不存在");
        }
        
        // 方式2:提供默认值
        String name = userName.orElse("默认用户");
        System.out.println("用户名称: " + name);
        
        // 方式3:执行操作(如果存在)
        userName.ifPresent(nameStr -> System.out.println("处理用户: " + nameStr));
    }
}

五、综合使用示例

下面是一个综合使用多种注解的实际例子:

import java.lang.annotation.*;
import java.lang.reflect.Method;

// 自定义注解:用于标记需要权限验证的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface RequirePermission {
    String[] roles() default {"USER"};
    boolean checkLogin() default true;
}

// 服务类
class SecureService {
    
    @RequirePermission(roles = {"ADMIN", "MANAGER"}, checkLogin = true)
    @Override
    public String toString() {
        return "安全服务实例";
    }
    
    @RequirePermission(roles = {"USER"})
    public void normalOperation() {
        System.out.println("执行普通操作");
    }
    
    @Deprecated(since = "2.0")
    public void oldOperation() {
        System.out.println("执行过时的操作");
    }
}

// 注解处理器(模拟权限检查)
class AnnotationProcessor {
    public static void main(String[] args) throws Exception {
        Class<?> serviceClass = SecureService.class;
        SecureService service = new SecureService();
        
        // 遍历所有方法,检查 @RequirePermission 注解
        for (Method method : serviceClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(RequirePermission.class)) {
                RequirePermission permission = method.getAnnotation(RequirePermission.class);
                
                System.out.println("方法: " + method.getName());
                System.out.println("需要角色: " + String.join(", ", permission.roles()));
                System.out.println("需要登录: " + permission.checkLogin());
                System.out.println("---");
                
                // 模拟执行方法
                if (method.getParameterCount() == 0) {
                    method.invoke(service);
                }
            }
        }
    }
}

总结

必须掌握的核心注解:

  1. 编译器提示类

    • @Override:确保正确重写
    • @Deprecated:标记过时API
    • @SuppressWarnings:抑制编译警告
  2. 元注解

    • @Target:指定注解使用位置
    • @Retention:指定注解保留策略
    • @Documented:控制JavaDoc包含
    • @Inherited:控制注解继承
    • @Repeatable:支持重复注解
  3. 函数式编程

    • @FunctionalInterface:标记函数式接口

学习建议:

  1. 优先掌握@Override@Deprecated@SuppressWarnings 这三个是最常用的
  2. 理解原理:元注解是自定义注解的基础,必须理解其工作原理
  3. 实践应用:在实际项目中尝试使用这些注解,特别是运行时注解配合反射
  4. 注意版本:某些注解需要特定的Java版本(如@Repeatable需要Java 8+)

掌握这些JDK原生注解,不仅能写出更规范、更安全的代码,还能为后续学习Spring等框架的注解机制打下坚实基础。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值