Java注解(Annotation)全面学习指南

Java注解(Annotation)全面学习指南

一、注解基础概念

1.1 什么是注解

  • 代码级别的元数据(metadata)
  • 为代码添加信息标签,不影响程序逻辑
  • JDK 1.5引入的特性

1.2 注解的作用

  • 编译检查(如@Override)
  • 代码分析(生成文档、代码分析)
  • 编译时和运行时处理
  • 框架配置(如Spring、JPA)

二、内置注解

2.1 编译相关注解

java 复制代码
// 1. @Override - 检查方法重写
@Override
public String toString() {
    return "This is overridden method";
}

// 2. @Deprecated - 标记已过时
@Deprecated
public void oldMethod() {
    // 不推荐使用
}

// 3. @SuppressWarnings - 抑制警告
@SuppressWarnings("unchecked")
List<String> list = new ArrayList();

// 4. @FunctionalInterface - 函数式接口(Java 8+)
@FunctionalInterface
interface MyFunctionalInterface {
    void execute();
}

2.2 元注解(用于定义注解的注解)

java 复制代码
import java.lang.annotation.*;

// 1. @Target - 指定注解使用位置
@Target({
    ElementType.TYPE,       // 类、接口、枚举
    ElementType.FIELD,      // 字段
    ElementType.METHOD,     // 方法
    ElementType.PARAMETER,  // 参数
    ElementType.CONSTRUCTOR,// 构造器
    ElementType.LOCAL_VARIABLE, // 局部变量
    ElementType.ANNOTATION_TYPE, // 注解类型
    ElementType.PACKAGE,    // 包
    ElementType.TYPE_PARAMETER, // 类型参数(Java 8+)
    ElementType.TYPE_USE    // 类型使用(Java 8+)
})

// 2. @Retention - 指定注解保留策略
@Retention(RetentionPolicy.SOURCE)  // 仅源码保留
@Retention(RetentionPolicy.CLASS)   // 编译时保留(默认)
@Retention(RetentionPolicy.RUNTIME) // 运行时保留(可通过反射获取)

// 3. @Documented - 包含在Javadoc中
@Documented

// 4. @Inherited - 允许子类继承
@Inherited

// 5. @Repeatable - 可重复注解(Java 8+)
@Repeatable

三、自定义注解

3.1 基本语法

java 复制代码
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 注解元素(看起来像方法,实际上是属性)
    String value() default "default";  // 默认值
    int count() default 0;
    String[] tags() default {};
    Class<?> clazz() default Object.class;
    RetentionPolicy policy() default RetentionPolicy.RUNTIME;
}

// 使用注解
public class AnnotationDemo {
    @MyAnnotation(value = "test", count = 5, tags = {"java", "annotation"})
    public void myMethod() {
        // 方法体
    }
}

3.2 注解元素类型限制

  • 基本数据类型(int, float, boolean等)
  • String
  • Class
  • Enum
  • Annotation
  • 以上类型的数组

不允许:null作为默认值

四、注解处理

4.1 运行时处理(反射)

java 复制代码
import java.lang.reflect.*;

// 定义运行时注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
    String name();
    String version() default "1.0";
}

// 使用注解
@CustomAnnotation(name = "DemoClass", version = "2.0")
class DemoClass {
    @CustomAnnotation(name = "demoMethod")
    public void demoMethod() {}
}

// 处理注解
public class AnnotationProcessor {
    public static void main(String[] args) {
        // 类注解处理
        Class<DemoClass> clazz = DemoClass.class;
        
        // 检查类是否有注解
        if (clazz.isAnnotationPresent(CustomAnnotation.class)) {
            CustomAnnotation annotation = clazz.getAnnotation(CustomAnnotation.class);
            System.out.println("Class: " + annotation.name());
            System.out.println("Version: " + annotation.version());
        }
        
        // 方法注解处理
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(CustomAnnotation.class)) {
                CustomAnnotation annotation = method.getAnnotation(CustomAnnotation.class);
                System.out.println("Method: " + method.getName());
                System.out.println("Annotation name: " + annotation.name());
            }
        }
        
        // 获取所有注解(包括继承的)
        Annotation[] annotations = clazz.getAnnotations();
        
        // 获取直接声明的注解
        Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
    }
}

4.2 编译时处理(APT - Annotation Processing Tool)

java 复制代码
// 1. 定义Processor
@SupportedAnnotationTypes({"com.example.*"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, 
                          RoundEnvironment roundEnv) {
        // 处理注解逻辑
        for (TypeElement annotation : annotations) {
            Set<? extends Element> elements = 
                roundEnv.getElementsAnnotatedWith(annotation);
            
            for (Element element : elements) {
                // 生成代码或执行检查
                processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.NOTE, 
                    "Found annotation on: " + element.getSimpleName()
                );
            }
        }
        return true;
    }
}

五、高级特性

5.1 重复注解(Java 8+)

java 复制代码
// 1. 定义容器注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Authors {
    Author[] value();
}

// 2. 定义可重复注解
@Repeatable(Authors.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Author {
    String name();
    String date();
}

// 3. 使用
@Author(name = "Alice", date = "2024-01-01")
@Author(name = "Bob", date = "2024-01-02")
class Book {
    // 类定义
}

5.2 类型注解(Java 8+)

java 复制代码
// 类型参数注解
class TypeAnnotationDemo<@NotEmpty T> {
    // 类型使用注解
    List<@NotEmpty String> list;
    
    // 方法引用注解
    void process(@NotNull String input) {
        // 类型转换注解
        String str = (@NotNull String) obj;
    }
}

5.3 注解继承

java 复制代码
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface InheritedAnnotation {
    String value();
}

@InheritedAnnotation("parent")
class ParentClass {}

// ChildClass会自动继承@InheritedAnnotation
class ChildClass extends ParentClass {}

六、实际应用示例

6.1 自定义验证注解

java 复制代码
// 定义验证注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Validation {
    int min() default 0;
    int max() default Integer.MAX_VALUE;
    boolean required() default false;
    String regex() default "";
}

// 使用验证注解
class User {
    @Validation(required = true, min = 2, max = 50)
    private String username;
    
    @Validation(required = true, regex = "^(?=.*[A-Za-z])(?=.*\\d).{8,}$")
    private String password;
    
    @Validation(min = 18, max = 100)
    private int age;
}

// 验证器
class Validator {
    public static List<String> validate(Object obj) throws IllegalAccessException {
        List<String> errors = new ArrayList<>();
        Class<?> clazz = obj.getClass();
        
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Validation.class)) {
                Validation validation = field.getAnnotation(Validation.class);
                Object value = field.get(obj);
                
                if (validation.required() && value == null) {
                    errors.add(field.getName() + " is required");
                }
                
                if (value instanceof String) {
                    String str = (String) value;
                    if (validation.required() && str.trim().isEmpty()) {
                        errors.add(field.getName() + " cannot be empty");
                    }
                    if (str.length() < validation.min()) {
                        errors.add(field.getName() + " is too short");
                    }
                    if (str.length() > validation.max()) {
                        errors.add(field.getName() + " is too long");
                    }
                    if (!validation.regex().isEmpty() && !str.matches(validation.regex())) {
                        errors.add(field.getName() + " format is invalid");
                    }
                }
                
                if (value instanceof Integer) {
                    int num = (int) value;
                    if (num < validation.min()) {
                        errors.add(field.getName() + " is too small");
                    }
                    if (num > validation.max()) {
                        errors.add(field.getName() + " is too large");
                    }
                }
            }
        }
        return errors;
    }
}

6.2 日志注解

java 复制代码
// 日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface LogExecution {
    String level() default "INFO";
    boolean logParams() default true;
    boolean logResult() default false;
}

// AOP处理(使用Spring AOP示例)
@Aspect
@Component
class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
    
    @Around("@annotation(logExecution)")
    public Object logMethod(ProceedingJoinPoint joinPoint, LogExecution logExecution) 
            throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        
        // 记录参数
        if (logExecution.logParams()) {
            Object[] args = joinPoint.getArgs();
            logger.log(Level.parse(logExecution.level()), 
                      "Method {} called with params: {}", 
                      methodName, Arrays.toString(args));
        }
        
        // 执行方法
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        
        // 记录结果
        if (logExecution.logResult()) {
            logger.log(Level.parse(logExecution.level()), 
                      "Method {} returned: {}, execution time: {}ms", 
                      methodName, result, endTime - startTime);
        } else {
            logger.log(Level.parse(logExecution.level()), 
                      "Method {} executed in {}ms", 
                      methodName, endTime - startTime);
        }
        
        return result;
    }
}

七、常见框架中的注解

7.1 Spring Framework

java 复制代码
// 常用Spring注解
@Component      // 通用组件
@Repository     // 数据访问层
@Service        // 业务层
@Controller     // 控制层
@RestController // REST控制器

@Autowired      // 自动注入
@Qualifier      // 指定Bean名称
@Value          // 注入值

@RequestMapping // 请求映射
@GetMapping     // GET请求
@PostMapping    // POST请求
@RequestBody    // 请求体
@ResponseBody   // 响应体

7.2 JPA/Hibernate

java 复制代码
@Entity         // 实体类
@Table          // 表映射
@Id             // 主键
@GeneratedValue // 主键生成策略
@Column         // 列映射
@OneToMany      // 一对多
@ManyToOne      // 多对一
@ManyToMany     // 多对多

八、最佳实践

8.1 设计原则

  1. 单一职责:每个注解应该有明确单一的目的
  2. 命名清晰:使用明确、描述性的名称
  3. 合理默认值:为可选参数提供合理的默认值
  4. 文档完善:使用@Documented和JavaDoc

8.2 性能考虑

  • 运行时注解通过反射获取,可能有性能开销
  • 频繁使用的注解应考虑缓存
  • 编译时注解无运行时开销

8.3 兼容性

  • 考虑注解的继承性
  • 避免破坏性更改
  • 提供合理的默认值以保持向后兼容

九、学习资源

9.1 官方文档

9.2 实践建议

  1. 从使用内置注解开始
  2. 尝试自定义简单注解
  3. 学习使用反射处理注解
  4. 研究主流框架的注解实现
  5. 实践编译时注解处理

十、总结

Java注解是现代Java开发的核心特性,广泛应用于:

  • 框架配置和元数据
  • 代码生成和检查
  • AOP编程
  • 测试框架
  • 序列化和验证

掌握注解的使用和自定义,能够显著提升代码质量和开发效率。建议结合实际项目需求,逐步深入学习和应用。

相关推荐
开开心心就好2 小时前
PDF密码移除工具,免费解除打印编辑复制权限
java·网络·windows·websocket·pdf·电脑·excel
咕噜企业分发小米2 小时前
豆包大模型在药物研发中的知识检索效率如何?
java·开发语言·数据库
Remember_9932 小时前
【LeetCode精选算法】二分查找专题一
java·数据结构·算法·spring·leetcode·哈希算法
BlockChain8882 小时前
Web3 后端面试专用版
java·面试·职场和发展·go·web3
BlockChain8882 小时前
30+ 技术人转型 Web3 / AI
java·人工智能·go·web3
Kratzdisteln2 小时前
【1902】0120-3 Dify变量引用只能引用一层
android·java·javascript
秋刀鱼程序编程2 小时前
Java基础入门(七)---异常处理
java·开发语言·python
遇见你的雩风2 小时前
Java---多线程(一)
java·开发语言
这就是佬们吗2 小时前
力扣---leetcode48
java·笔记·后端·算法·leetcode·idea