Java学习笔记:注解详解

Java注解详解与实战应用

Java注解(Annotation)是从JDK 5.0开始引入的重要特性,它为代码提供了元数据支持,可以在编译时、运行时或开发工具中使用。注解广泛应用于框架配置、代码生成、依赖注入等方面。今天详细介绍Java注解的概念、使用方法、自定义注解以及在实际开发中的应用。

注解的基本概念

什么是注解
注解是一种特殊的Java类型,它以@符号开头,可以为代码添加元数据信息。注解本身不影响代码的执行,但可以通过反射或其他工具读取和处理这些元数据。

注解的分类
内置注解:Java语言本身提供的注解
元注解:用于定义其他注解的注解
自定义注解:开发者定义的注解

注解的作用
编译时检查:如@Override检查方法重写
代码生成:如Lombok生成getter/setter方法
配置信息:如Spring框架的@Configuration注解
运行时处理:如JUnit测试注解

内置注解

@Override
用于标记方法重写父类方法,编译器会检查方法签名是否正确。

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

publicclass Child extends Parent {
    @Override
    public void display() {
        System.out.println("Child display");
    }

    // 编译错误:方法不存在于父类中
    // @Override
    // public void show() {
    // }
}

@Deprecated
标记方法、类或字段已过时,建议不要使用。

public class DeprecatedExample {
    @Deprecated
    public static void oldMethod() {
        System.out.println("这个方法已过时");
    }

    @Deprecated(since = "1.2", forRemoval = true)
    public static void veryOldMethod() {
        System.out.println("这个方法即将被移除");
    }

    public static void main(String[] args) {
        // 使用已过时的方法会产生警告
        oldMethod();
        veryOldMethod();
    }
}

@SuppressWarnings
抑制编译器警告。

import java.util.ArrayList;
import java.util.List;

publicclass SuppressWarningsExample {
    @SuppressWarnings("unchecked")
    public static void uncheckedCast() {
        List rawList = new ArrayList();
        List<String> stringList = (List<String>) rawList; // 通常会产生unchecked警告
    }

    @SuppressWarnings({"unchecked", "unused"})
    public static void multipleWarnings() {
        List rawList = new ArrayList();
        List<String> stringList = (List<String>) rawList;

        int unusedVariable = 42; // 不会产生unused警告
    }

    @SuppressWarnings("all")
    public static void suppressAll() {
        // 抑制所有警告
    }
}

@SafeVarargs (Java 7+)
用于标记可变参数方法是安全的,不会产生堆污染警告。

import java.util.Arrays;
import java.util.List;

publicclass SafeVarargsExample {
    @SafeVarargs
    publicstatic <T> List<T> asList(T... elements) {
        return Arrays.asList(elements);
    }

    public static void main(String[] args) {
        List<String> list = asList("A", "B", "C");
        System.out.println(list);
    }
}

@FunctionalInterface (Java 8+)
标记接口为函数式接口,确保只包含一个抽象方法。

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);

    // 默认方法不计入抽象方法数量
    default void display() {
        System.out.println("Calculator");
    }

    // 静态方法不计入抽象方法数量
    static void info() {
        System.out.println("这是一个计算器接口");
    }

    // 编译错误:函数式接口只能有一个抽象方法
    // void anotherMethod();
}

元注解

元注解用于定义其他注解的行为。

@Retention
指定注解的保留策略:

RetentionPolicy.SOURCE:只在源代码中保留,编译时丢弃
RetentionPolicy.CLASS:保留在class文件中,但JVM运行时不保留(默认)
RetentionPolicy.RUNTIME:保留到运行时,可以通过反射获取

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

@Retention(RetentionPolicy.SOURCE)
@interface SourceAnnotation {
}

@Retention(RetentionPolicy.CLASS)
@interface ClassAnnotation {
}

@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeAnnotation {
}

@Target
指定注解可以应用的目标元素:

ElementType.TYPE:类、接口、枚举
ElementType.FIELD:字段
ElementType.METHOD:方法
ElementType.PARAMETER:方法参数
ElementType.CONSTRUCTOR:构造方法
ElementType.LOCAL_VARIABLE:局部变量
ElementType.ANNOTATION_TYPE:注解类型
ElementType.PACKAGE:包

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

@Target(ElementType.TYPE)
@interface ClassAnnotation {
}

@Target(ElementType.FIELD)
@interface FieldAnnotation {
}

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@interface MethodAnnotation {
}

@Target(ElementType.PARAMETER)
@interface ParameterAnnotation {
}

@Documented
指定注解是否包含在JavaDoc中。

import java.lang.annotation.Documented;

@Documented
@interface DocumentedAnnotation {
    String value();
}

// 这个注解会出现在生成的JavaDoc中

@Inherited
指定注解是否可以被子类继承。

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

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface InheritableAnnotation {
    String value();
}

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

class Child extends Parent {
    public static void main(String[] args) {
        // 子类会继承父类的注解
        InheritableAnnotation annotation = Child.class.getAnnotation(InheritableAnnotation.class);
        if (annotation != null) {
            System.out.println("继承的注解值:" + annotation.value());
        }
    }
}

@Repeatable (Java 8+)
允许注解在同一元素上重复使用。

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

@Retention(RetentionPolicy.RUNTIME)
@interface Author {
    String name();
}

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Authors.class)
@interface Authors {
    Author[] value();
}

@Author(name = "张三")
@Author(name = "李四")
class Book {
    public static void main(String[] args) {
        Author[] authors = Book.class.getAnnotationsByType(Author.class);
        for (Author author : authors) {
            System.out.println("作者:" + author.name());
        }
    }
}

自定义注解

基本语法

// 定义注解
@interface MyAnnotation {
    String value();  // 必需属性
    int count() default 1;  // 可选属性,默认值为1
    String[] tags() default {};  // 数组属性
}

// 使用注解
@MyAnnotation(value = "测试", count = 5, tags = {"tag1", "tag2"})
publicclass MyClass {
    @MyAnnotation("字段注解")
    private String field;

    @MyAnnotation(value = "方法注解", count = 3)
    public void myMethod() {
    }
}

注解属性类型

注解属性可以是以下类型:

基本数据类型(int, long, float, double, boolean, char, byte, short)
String
Class
枚举类型
注解类型
以上类型的数组

@interface ComplexAnnotation {
    // 基本类型
    int intValue() default 0;
    boolean boolValue() default false;

    // 字符串
    String stringValue() default "";

    // Class类型
    Class<?> classValue() default Object.class;

    // 枚举
    enum Level {LOW, MEDIUM, HIGH}
    Level level() default Level.MEDIUM;

    // 嵌套注解
    SimpleAnnotation simple() default @SimpleAnnotation("default");

    // 数组
    String[] stringArray() default {};
    int[] intArray() default {};
}

@interface SimpleAnnotation {
    String value();
}

注解处理

运行时通过反射处理注解

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@interface MyRuntimeAnnotation {
    String value();
    int priority() default 1;
}

@MyRuntimeAnnotation(value = "测试类", priority = 5)
publicclass AnnotationProcessor {

    @MyRuntimeAnnotation("测试字段")
    private String testField;

    @MyRuntimeAnnotation(value = "测试方法", priority = 3)
    public void testMethod() {
    }

    public static void main(String[] args) {
        Class<?> clazz = AnnotationProcessor.class;

        // 处理类级注解
        if (clazz.isAnnotationPresent(MyRuntimeAnnotation.class)) {
            MyRuntimeAnnotation classAnnotation = clazz.getAnnotation(MyRuntimeAnnotation.class);
            System.out.println("类注解:" + classAnnotation.value() + ", 优先级:" + classAnnotation.priority());
        }

        // 处理字段注解
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(MyRuntimeAnnotation.class)) {
                MyRuntimeAnnotation fieldAnnotation = field.getAnnotation(MyRuntimeAnnotation.class);
                System.out.println("字段注解:" + fieldAnnotation.value());
            }
        }

        // 处理方法注解
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyRuntimeAnnotation.class)) {
                MyRuntimeAnnotation methodAnnotation = method.getAnnotation(MyRuntimeAnnotation.class);
                System.out.println("方法注解:" + methodAnnotation.value() + ", 优先级:" + methodAnnotation.priority());
            }
        }
    }
}

编译时注解处理
编译时注解处理需要使用Annotation Processor API。

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;

@SupportedAnnotationTypes("com.example.MyCompileAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
publicclass MyAnnotationProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement annotation : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
                // 处理注解元素,生成代码等
                System.out.println("处理注解元素:" + element.getSimpleName());
            }
        }
        returntrue;
    }
}

注解在框架中的应用

Spring框架注解示例

import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

@Component
publicclass UserService {

    @Value("${app.user.default-age}")
    privateint defaultAge;

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public User createUser(@Valid CreateUserRequest request) {
        // 创建用户逻辑
    }
}

@RestController
@RequestMapping("/api/users")
publicclass UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    @PostMapping
    public User createUser(@RequestBody @Valid CreateUserRequest request) {
        return userService.createUser(request);
    }
}

JUnit测试注解示例

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
public class CalculatorTest {

    @BeforeAll
    static void setUpAll() {
        System.out.println("在所有测试前执行一次");
    }

    @BeforeEach
    void setUp() {
        System.out.println("在每个测试前执行");
    }

    @Test
    @DisplayName("测试加法运算")
    void testAddition() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result);
    }

    @Test
    @Disabled("暂时跳过这个测试")
    void testDisabled() {
        // 这个测试会被跳过
    }

    @AfterEach
    void tearDown() {
        System.out.println("在每个测试后执行");
    }

    @AfterAll
    static void tearDownAll() {
        System.out.println("在所有测试后执行一次");
    }
}

Lombok注解示例

Lombok使用注解在编译时生成代码。

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;

@Data// 生成getter、setter、equals、hashCode、toString方法
@NoArgsConstructor// 生成无参构造方法
@AllArgsConstructor// 生成全参构造方法
@Builder// 生成建造者模式
publicclass User {
    private Long id;
    private String name;
    private String email;
    privateint age;

    // Lombok会自动生成所有这些方法
    // public User() {}
    // public User(Long id, String name, String email, int age) {}
    // public Long getId() { return id; }
    // public void setId(Long id) { this.id = id; }
    // ... 其他getter和setter
    // public boolean equals(Object o) { ... }
    // public int hashCode() { ... }
    // public String toString() { ... }
    // public UserBuilder builder() { ... }
}

注解最佳实践

1. 合理使用@Retention

// 只在编译时需要的注解使用SOURCE
@Retention(RetentionPolicy.SOURCE)
@interface CompileTimeAnnotation {
}

// 需要运行时反射的注解使用RUNTIME
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeAnnotation {
}

2. 提供有意义的默认值

@interface ConfigAnnotation {
    String name() default "";  // 有意义的默认值
    int timeout() default 30;  // 合理的默认超时时间
    String[] tags() default {};  // 空数组作为默认值
}

3. 使用@Documented为库注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyLibraryAnnotation {
    String description();
}

4. 避免过度使用注解

// 不推荐:过度使用注解
@MyAnnotation1
@MyAnnotation2
@MyAnnotation3
publicclass OverAnnotatedClass {
    @MyAnnotation4
    @MyAnnotation5
    private String field;
}

// 推荐:合并相关注解或使用配置类
@MyCombinedAnnotation
publicclass ProperlyAnnotatedClass {
    @MyFieldAnnotation
    private String field;
}

5. 为注解编写文档

/**
 * 用于标记需要特殊处理的类
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SpecialProcessing {
    /**
     * 处理器的名称
     * @return 处理器名称
     */
    String processorName() default "default";

    /**
     * 处理优先级,数字越大优先级越高
     * @return 优先级
     */
    int priority() default 0;
}

总结

Java注解是现代Java开发的重要组成部分,它为代码提供了丰富的元数据支持。通过合理使用注解,可以提高代码的可读性、可维护性和开发效率。

关键要点:

内置注解:@Override、@Deprecated、@SuppressWarnings等
元注解:@Retention、@Target、@Documented、@Inherited等
自定义注解:定义自己的注解来满足特定需求
注解处理:通过反射或编译时处理来使用注解信息
框架应用:Spring、JUnit等框架大量使用注解
最佳实践:合理选择保留策略,提供有意义的默认值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小马不敲代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值