← 返回首页
🏷️

Java注解详解:定义、使用与自定义注解

📂 java ⏱ 5 min 890 words

Java注解详解:定义、使用与自定义注解

概述

注解(Annotation)是Java 5引入的元数据机制,它可以添加到代码元素(类、方法、字段等)上,提供额外的信息。注解不会直接影响代码的执行,但可以被编译器、运行时环境或框架读取和处理。

1. 内置注解

import java.util.*;

public class BuiltInAnnotations {
    // @Override:标记重写父类方法
    @Override
    public String toString() {
        return "BuiltInAnnotations{}";
    }
    
    // @Deprecated:标记已弃用
    @Deprecated
    public void oldMethod() {
        System.out.println("This method is deprecated");
    }
    
    // @SuppressWarnings:抑制警告
    @SuppressWarnings("unchecked")
    public void uncheckedOperation() {
        List list = new ArrayList();
        list.add("Hello");
        String str = (String) list.get(0);
    }
    
    // @FunctionalInterface:标记函数式接口
    @FunctionalInterface
    public interface Calculator {
        int calculate(int a, int b);
    }
    
    // @SafeVarargs:安全可变参数
    @SafeVarargs
    public static <T> List<T> asList(T... elements) {
        return Arrays.asList(elements);
    }
    
    public static void main(String[] args) {
        BuiltInAnnotations obj = new BuiltInAnnotations();
        System.out.println(obj);
        
        // 使用deprecated方法(会产生警告)
        obj.oldMethod();
    }
}

2. 自定义注解

基本自定义注解

import java.lang.annotation.*;

// 定义自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    String value() default "";
    int priority() default 0;
    String[] tags() default {};
}

// 使用自定义注解
public class AnnotationUser {
    @MyAnnotation(value = "Hello", priority = 1, tags = {"tag1", "tag2"})
    public void annotatedMethod() {
        System.out.println("被注解的方法");
    }
    
    @MyAnnotation(value = "World")
    public void anotherMethod() {
        System.out.println("另一个被注解的方法");
    }
    
    public void normalMethod() {
        System.out.println("普通方法");
    }
}

注解的元注解

import java.lang.annotation.*;

// @Retention:定义注解的保留策略
@Retention(RetentionPolicy.RUNTIME)  // RUNTIME:运行时保留
@Target(ElementType.TYPE)  // @Target:定义注解可以应用的元素
@Documented  // @Documented:包含在JavaDoc中
@Inherited  // @Inherited:子类继承父类的注解
public @interface MyAnnotation2 {
    String value() default "";
}

// RetentionPolicy三种策略:
// SOURCE:只在源码中保留,编译后丢失
// CLASS:在class文件中保留,运行时丢失
// RUNTIME:运行时保留,可以通过反射获取

// ElementType多种类型:
// TYPE:类、接口、枚举
// METHOD:方法
// FIELD:字段
// CONSTRUCTOR:构造方法
// PARAMETER:参数
// PACKAGE:包

3. 注解处理

运行时注解处理(反射)

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

// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Table {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
    String value();
    boolean nullable() default true;
    int length() default 255;
}

// 使用注解的实体类
@Table("users")
class User {
    @Column(value = "id", nullable = false)
    private Long id;
    
    @Column(value = "name", length = 100)
    private String name;
    
    @Column(value = "email", nullable = false, length = 200)
    private String email;
    
    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    // Getter和Setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

// 注解处理器
public class AnnotationProcessor {
    public static String generateSQL(Class<?> clazz) {
        StringBuilder sql = new StringBuilder();
        
        // 检查类注解
        if (clazz.isAnnotationPresent(Table.class)) {
            Table table = clazz.getAnnotation(Table.class);
            sql.append("CREATE TABLE ").append(table.value()).append(" (\n");
            
            // 处理字段注解
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if (field.isAnnotationPresent(Column.class)) {
                    Column column = field.getAnnotation(Column.class);
                    sql.append("    ").append(column.value()).append(" ");
                    
                    // 根据字段类型生成SQL类型
                    sql.append(getSQLType(field.getType()));
                    
                    if (!column.nullable()) {
                        sql.append(" NOT NULL");
                    }
                    
                    if (i < fields.length - 1) {
                        sql.append(",");
                    }
                    sql.append("\n");
                }
            }
            
            sql.append(");");
        }
        
        return sql.toString();
    }
    
    private static String getSQLType(Class<?> type) {
        if (type == Long.class || type == long.class) {
            return "BIGINT";
        } else if (type == Integer.class || type == int.class) {
            return "INT";
        } else if (type == String.class) {
            return "VARCHAR(255)";
        } else if (type == Boolean.class || type == boolean.class) {
            return "BOOLEAN";
        }
        return "TEXT";
    }
    
    public static void main(String[] args) {
        String sql = generateSQL(User.class);
        System.out.println("生成的SQL:");
        System.out.println(sql);
    }
}

4. 编译时注解处理

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

// 编译时注解处理器
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
@interface Log {
    String level() default "INFO";
}

class Service {
    @Log(level = "DEBUG")
    public void processData() {
        System.out.println("Processing data...");
    }
    
    public void processMoreData() {
        System.out.println("Processing more data...");
    }
}

// 注解处理器(简化示例)
public class CompileTimeProcessor {
    public static void process(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        
        for (Method method : methods) {
            if (method.isAnnotationPresent(Log.class)) {
                Log log = method.getAnnotation(Log.class);
                System.out.println("Method: " + method.getName());
                System.out.println("Log level: " + log.level());
            }
        }
    }
    
    public static void main(String[] args) {
        process(Service.class);
    }
}

5. 实际应用示例

验证框架

import java.lang.annotation.*;
import java.util.*;

// 验证注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface NotNull {
    String message() default "不能为空";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Size {
    int min() default 0;
    int max() default Integer.MAX_VALUE;
    String message() default "长度不符合要求";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Email {
    String message() default "邮箱格式不正确";
}

// 验证异常
class ValidationException extends Exception {
    public ValidationException(String message) {
        super(message);
    }
}

// 验证器
class Validator {
    public static void validate(Object obj) throws ValidationException {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        
        for (Field field : fields) {
            field.setAccessible(true);
            
            try {
                Object value = field.get(obj);
                
                // NotNull验证
                if (field.isAnnotationPresent(NotNull.class)) {
                    if (value == null) {
                        NotNull notNull = field.getAnnotation(NotNull.class);
                        throw new ValidationException(notNull.message());
                    }
                }
                
                // Size验证
                if (field.isAnnotationPresent(Size.class) && value instanceof String) {
                    Size size = field.getAnnotation(Size.class);
                    String str = (String) value;
                    if (str.length() < size.min() || str.length() > size.max()) {
                        throw new ValidationException(size.message());
                    }
                }
                
                // Email验证
                if (field.isAnnotationPresent(Email.class) && value instanceof String) {
                    Email email = field.getAnnotation(Email.class);
                    String str = (String) value;
                    if (!str.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
                        throw new ValidationException(email.message());
                    }
                }
                
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

// 使用示例
class UserForm {
    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;
    
    @NotNull(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    public UserForm(String username, String email) {
        this.username = username;
        this.email = email;
    }
}

public class ValidationExample {
    public static void main(String[] args) {
        // 测试有效数据
        try {
            UserForm validUser = new UserForm("alice", "alice@example.com");
            Validator.validate(validUser);
            System.out.println("验证通过");
        } catch (ValidationException e) {
            System.out.println("验证失败: " + e.getMessage());
        }
        
        // 测试无效数据
        try {
            UserForm invalidUser = new UserForm("ab", "invalid-email");
            Validator.validate(invalidUser);
            System.out.println("验证通过");
        } catch (ValidationException e) {
            System.out.println("验证失败: " + e.getMessage());
        }
    }
}

6. 最佳实践

  1. 选择合适的保留策略:根据使用场景选择SOURCE、CLASS或RUNTIME
  2. 合理使用元注解:@Target、@Retention、@Documented等
  3. 提供默认值:注解元素应该有合理的默认值
  4. 保持简单:注解应该简洁明了
  5. 文档化注解:清楚地文档化注解的用途和元素

总结

注解是Java中强大的元数据机制,它提供了在代码中添加额外信息的方式。通过注解,可以实现编译时检查、运行时处理等功能。掌握注解的定义、使用和处理,是编写高质量Java程序的重要技能。