Skip to content

注解

注解(Annotation)是 Java 5 引入的一种元数据形式,可以在代码中添加信息,用于标记类、方法、字段等元素。

注解概述

什么是注解

text
┌─────────────────────────────────────────────────────────────────┐
│                       注解概述                                   │
├─────────────────────────────────────────────────────────────────┤
│  注解是一种特殊的接口,以 @ 开头                                  │
│                                                                 │
│  注解的作用:                                                    │
│    1. 编译检查:如 @Override 检查方法重写                        │
│    2. 代码分析:通过反射获取注解信息                             │
│    3. 生成文档:如 @param、@return                              │
│    4. 运行时处理:框架通过注解实现功能                           │
│                                                                 │
│  注解 vs 注释:                                                  │
│    - 注释:给人看的,编译后忽略                                  │
│    - 注解:给程序看的,编译后保留                                │
└─────────────────────────────────────────────────────────────────┘

注解语法

java
// 注解的基本语法
@AnnotationName
public class MyClass {
    
    @AnnotationName(parameter = "value")
    public void method() {
    }
    
    @AnnotationName(param1 = "value1", param2 = "value2")
    public void anotherMethod() {
    }
    
    // 无参数时可以省略括号
    @Override
    public String toString() {
        return "MyClass";
    }
}

内置注解

@Override

java
public class OverrideDemo {
    public static void main(String[] args) {
        Child child = new Child();
        child.show();
    }
}

class Parent {
    public void show() {
        System.out.println("Parent show");
    }
}

class Child extends Parent {
    // @Override:标记方法重写,编译器检查是否正确重写
    @Override
    public void show() {
        System.out.println("Child show");
    }
    
    // 如果方法名拼写错误,编译器会报错
    // @Override
    // public void shows() { }  // 编译错误:方法未重写
}

@Deprecated

java
public class DeprecatedDemo {
    public static void main(String[] args) {
        OldClass old = new OldClass();
        
        // 使用 @Deprecated 标记的方法会产生警告
        old.oldMethod();  // 警告:已过时
        
        old.newMethod();  // 正常使用
    }
}

class OldClass {
    // @Deprecated:标记已过时的元素
    @Deprecated(since = "2.0", forRemoval = false)
    public void oldMethod() {
        System.out.println("旧方法,不建议使用");
    }
    
    public void newMethod() {
        System.out.println("新方法,推荐使用");
    }
}

@SuppressWarnings

java
import java.util.*;

public class SuppressWarningsDemo {
    // @SuppressWarnings:抑制编译器警告
    @SuppressWarnings({"unchecked", "deprecation"})
    public static void main(String[] args) {
        // 原始类型警告
        List list = new ArrayList();
        list.add("Hello");
        
        // 使用过时方法
        OldClass old = new OldClass();
        old.oldMethod();
    }
    
    // 抑制未使用警告
    @SuppressWarnings("unused")
    public void unusedMethod() {
        String unusedVariable = "未使用的变量";
    }
}

@FunctionalInterface

java
// @FunctionalInterface:标记函数式接口(只有一个抽象方法)
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
    
    // 可以有默认方法
    default void print() {
        System.out.println("Calculator");
    }
    
    // 可以有静态方法
    static Calculator add() {
        return (a, b) -> a + b;
    }
    
    // 只能有一个抽象方法
    // int anotherMethod();  // 编译错误
}

public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        Calculator add = (a, b) -> a + b;
        System.out.println("10 + 5 = " + add.calculate(10, 5));
        
        Calculator multiply = (a, b) -> a * b;
        System.out.println("10 * 5 = " + multiply.calculate(10, 5));
    }
}

元注解

元注解是用于定义注解的注解。

@Target

java
import java.lang.annotation.*;

// @Target:指定注解可以使用的位置
@Target({
    ElementType.TYPE,        // 类、接口、枚举
    ElementType.FIELD,       // 字段
    ElementType.METHOD,      // 方法
    ElementType.PARAMETER,   // 参数
    ElementType.CONSTRUCTOR, // 构造方法
    ElementType.LOCAL_VARIABLE, // 局部变量
    ElementType.ANNOTATION_TYPE, // 注解类型
    ElementType.PACKAGE      // 包
})
@interface MyAnnotation {
    String value() default "";
}

@Retention

java
import java.lang.annotation.*;

// @Retention:指定注解的保留策略
public enum RetentionPolicyDemo {
    
    // SOURCE:源码保留,编译时丢弃
    @Retention(RetentionPolicy.SOURCE)
    @interface SourceAnnotation { }
    
    // CLASS:字节码保留,运行时丢弃(默认)
    @Retention(RetentionPolicy.CLASS)
    @interface ClassAnnotation { }
    
    // RUNTIME:运行时保留,可通过反射获取
    @Retention(RetentionPolicy.RUNTIME)
    @interface RuntimeAnnotation { }
}

@Documented

java
import java.lang.annotation.*;

// @Documented:注解会被包含在 JavaDoc 中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ApiDoc {
    String description();
    String author();
}

@Inherited

java
import java.lang.annotation.*;

// @Inherited:注解可以被继承
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface InheritableAnnotation {
    String value();
}

@InheritableAnnotation("父类注解")
class ParentClass { }

// 子类会继承父类的 @InheritableAnnotation
class ChildClass extends ParentClass { }

// 非 @Inherited 的注解不会被继承
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface NonInheritedAnnotation {
    String value();
}

@Repeatable

java
import java.lang.annotation.*;

// Java 8+:可重复注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Author {
    String name();
}

// 容器注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Authors {
    Author[] value();
}

// 使用 @Repeatable
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Authors.class)
@interface Developer {
    String name();
}

// 使用可重复注解
@Developer(name = "张三")
@Developer(name = "李四")
@Developer(name = "王五")
class Project { }

public class RepeatableDemo {
    public static void main(String[] args) {
        Developer[] developers = Project.class.getAnnotationsByType(Developer.class);
        for (Developer dev : developers) {
            System.out.println("开发者:" + dev.name());
        }
    }
}

自定义注解

定义注解

java
import java.lang.annotation.*;

/**
 * 自定义注解:用于标记 API 接口
 */
@Retention(RetentionPolicy.RUNTIME)  // 运行时保留
@Target(ElementType.METHOD)         // 用于方法
public @interface Api {
    // 注解元素:类似接口方法
    String path();                    // 必须指定
    
    String method() default "GET";    // 有默认值,可选
    
    String description() default "";  // 有默认值,可选
    
    int timeout() default 5000;       // 有默认值,可选
    
    // 特殊值 value:如果只有一个元素且名为 value,可以省略元素名
    // String value();
}

使用注解

java
// 使用自定义注解
class UserController {
    
    @Api(path = "/users", method = "GET", description = "获取用户列表")
    public void getUsers() {
        System.out.println("获取用户列表");
    }
    
    @Api(path = "/users/{id}", method = "POST")
    public void createUser() {
        System.out.println("创建用户");
    }
    
    // 省略可选参数
    @Api(path = "/users/{id}")
    public void getUser() {
        System.out.println("获取单个用户");
    }
}

注解元素类型

java
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ElementTypes {
    // 允许的类型:
    // 1. 基本类型
    int intValue();
    double doubleValue();
    boolean booleanValue();
    
    // 2. String
    String stringValue();
    
    // 3. Class
    Class<?> classValue();
    
    // 4. 枚举
    Thread.State enumValue();
    
    // 5. 注解
    Deprecated annotationValue();
    
    // 6. 以上类型的一维数组
    String[] arrayValue();
    
    // 不允许的类型:
    // Object、包装类(Integer、Double等)、多维数组
}

注解处理

运行时处理

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Log {
    String value() default "";
    boolean enable() default true;
}

class Service {
    @Log("执行业务方法")
    public void businessMethod() {
        System.out.println("业务方法执行");
    }
    
    @Log(value = "调试方法", enable = false)
    public void debugMethod() {
        System.out.println("调试方法执行");
    }
    
    public void normalMethod() {
        System.out.println("普通方法");
    }
}

public class AnnotationProcessor {
    public static void main(String[] args) throws Exception {
        Service service = new Service();
        Class<?> clazz = Service.class;
        
        // 遍历所有方法
        for (Method method : clazz.getDeclaredMethods()) {
            // 检查方法是否有 @Log 注解
            if (method.isAnnotationPresent(Log.class)) {
                Log log = method.getAnnotation(Log.class);
                
                if (log.enable()) {
                    System.out.println("[日志] " + log.value());
                }
                
                // 执行方法
                method.invoke(service);
            } else {
                method.invoke(service);
            }
        }
    }
}

类级别注解处理

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Table {
    String name();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
    String name();
    boolean primaryKey() default false;
}

@Table(name = "t_user")
class User {
    @Column(name = "id", primaryKey = true)
    private Long id;
    
    @Column(name = "username")
    private String username;
    
    @Column(name = "email")
    private String email;
    
    // getter 和 setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

public class OrmDemo {
    public static void main(String[] args) {
        Class<User> clazz = User.class;
        
        // 获取表名
        Table table = clazz.getAnnotation(Table.class);
        System.out.println("表名:" + table.name());
        
        // 获取字段信息
        System.out.println("\n字段信息:");
        for (Field field : clazz.getDeclaredFields()) {
            Column column = field.getAnnotation(Column.class);
            if (column != null) {
                System.out.println(field.getName() + " -> " + column.name() + 
                    (column.primaryKey() ? " (主键)" : ""));
            }
        }
        
        // 生成 SQL
        StringBuilder sql = new StringBuilder("CREATE TABLE ");
        sql.append(table.name()).append(" (");
        
        boolean first = true;
        for (Field field : clazz.getDeclaredFields()) {
            Column column = field.getAnnotation(Column.class);
            if (column != null) {
                if (!first) sql.append(", ");
                sql.append(column.name()).append(" ");
                sql.append(getSqlType(field.getType()));
                if (column.primaryKey()) {
                    sql.append(" PRIMARY KEY");
                }
                first = false;
            }
        }
        sql.append(")");
        
        System.out.println("\n生成的 SQL:\n" + sql);
    }
    
    private static String getSqlType(Class<?> type) {
        if (type == Long.class || type == long.class) return "BIGINT";
        if (type == String.class) return "VARCHAR(255)";
        if (type == Integer.class || type == int.class) return "INT";
        return "VARCHAR(255)";
    }
}

字段验证注解

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

@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 Range {
    long min() default Long.MIN_VALUE;
    long max() default Long.MAX_VALUE;
    String message() default "数值不在有效范围内";
}

class Person {
    @NotNull(message = "姓名不能为空")
    @Size(min = 2, max = 20, message = "姓名长度应在2-20之间")
    private String name;
    
    @Range(min = 0, max = 150, message = "年龄应在0-150之间")
    private int age;
    
    @NotNull(message = "邮箱不能为空")
    private String email;
    
    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

public class Validator {
    public static void validate(Object obj) throws Exception {
        Class<?> clazz = obj.getClass();
        
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            Object value = field.get(obj);
            
            // @NotNull 验证
            NotNull notNull = field.getAnnotation(NotNull.class);
            if (notNull != null && value == null) {
                throw new IllegalArgumentException(
                    field.getName() + ": " + notNull.message());
            }
            
            // @Size 验证
            Size size = field.getAnnotation(Size.class);
            if (size != null && value instanceof String) {
                int length = ((String) value).length();
                if (length < size.min() || length > size.max()) {
                    throw new IllegalArgumentException(
                        field.getName() + ": " + size.message());
                }
            }
            
            // @Range 验证
            Range range = field.getAnnotation(Range.class);
            if (range != null && value instanceof Number) {
                long num = ((Number) value).longValue();
                if (num < range.min() || num > range.max()) {
                    throw new IllegalArgumentException(
                        field.getName() + ": " + range.message());
                }
            }
        }
    }
    
    public static void main(String[] args) {
        try {
            Person p1 = new Person("张三", 25, "zhangsan@example.com");
            validate(p1);
            System.out.println("验证通过");
        } catch (Exception e) {
            System.out.println("验证失败:" + e.getMessage());
        }
        
        try {
            Person p2 = new Person(null, 25, "test@example.com");
            validate(p2);
        } catch (Exception e) {
            System.out.println("验证失败:" + e.getMessage());
        }
        
        try {
            Person p3 = new Person("张", 25, "test@example.com");
            validate(p3);
        } catch (Exception e) {
            System.out.println("验证失败:" + e.getMessage());
        }
        
        try {
            Person p4 = new Person("张三", 200, "test@example.com");
            validate(p4);
        } catch (Exception e) {
            System.out.println("验证失败:" + e.getMessage());
        }
    }
}

注解最佳实践

命名约定

java
// 注解命名约定
// 1. 使用名词或形容词
// 2. 首字母大写,驼峰命名
// 3. 不使用动词

// 好的命名
@interface Entity { }
@interface Service { }
@interface Valid { }
@interface NotNull { }
@interface Injectable { }

// 不好的命名
@interface Validate { }  // 动词
@interface doSomething { }  // 小写开头

注解设计原则

java
import java.lang.annotation.*;

// 好的设计:职责单一,语义清晰
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Cacheable {
    String key();                    // 缓存键
    int expire() default 3600;       // 过期时间(秒)
    boolean async() default false;   // 是否异步
}

// 使用示例
class ProductService {
    @Cacheable(key = "product:{id}", expire = 7200)
    public Product getProduct(Long id) {
        // 查询数据库
        return new Product(id, "商品" + id);
    }
}

class Product {
    Long id;
    String name;
    public Product(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

小结

本章我们学习了:

  • 注解概述:注解的定义和作用
  • 内置注解:@Override、@Deprecated、@SuppressWarnings、@FunctionalInterface
  • 元注解:@Target、@Retention、@Documented、@Inherited、@Repeatable
  • 自定义注解:定义和使用自定义注解
  • 注解处理:运行时通过反射处理注解
  • 最佳实践:命名约定和设计原则

恭喜你完成了 Java 基础教程的学习!接下来你可以继续学习: