1. 引言

注解自 Java 5 引入以来,已成为无处不在的编程结构,用于丰富代码元数据。本文将整理面试中常见的注解相关问题,并通过示例深入理解其核心概念。

2. 问题

2.1. 什么是注解?典型用例有哪些?

注解是绑定到程序源代码元素的元数据,不影响代码本身运行逻辑。典型用例包括:

  • 编译器指令
    ✅ 检测错误或抑制警告(如 @Override 检查方法重写)
  • 编译/部署时处理
    ✅ 生成代码、配置文件(如 Lombok 自动生成 getter/setter)
  • 运行时处理
    ✅ 动态定制程序行为(如 Spring 的 @Autowired 依赖注入)

2.2. 列举标准库中的常用注解

java.langjava.lang.annotation 包中的核心注解:

  • @Override
    ✅ 标记方法重写父类声明,编译器验证正确性
  • @Deprecated
    ⚠️ 标记元素已过时,编译器发出警告
  • @SuppressWarnings
    ✅ 抑制编译器警告(常用于遗留代码兼容)
  • @FunctionalInterface
    ✅ 标识函数式接口(Java 8+),支持 Lambda 表达式

2.3. 如何创建自定义注解?

注解本质是特殊接口,使用 @interface 声明:

public @interface SimpleAnnotation {
    String value();  // 注解元素

    int[] types();  // 数组类型元素
}

使用时需指定所有元素(数组值用 {} 包裹):

@SimpleAnnotation(value = "an element", types = 1)
public class Element {
    @SimpleAnnotation(value = "an attribute", types = { 1, 2 })
    public Element nextElement;
}

设置默认值(常量表达式)后可省略:

public @interface SimpleAnnotation {
    String value() default "This is an element";
    int[] types() default { 1, 2, 3 };
}

// 使用默认值
@SimpleAnnotation
public class Element { ... }

// 部分覆盖
@SimpleAnnotation(value = "an attribute")
public Element nextElement;

2.4. 注解方法声明允许的返回类型有哪些?

仅限以下类型,否则编译报错:

✅ 基本类型(int, boolean 等)
String
Class
Enum
✅ 以上类型的数组

合法示例

enum Complexity { LOW, HIGH }

public @interface ComplexAnnotation {
    Class<? extends Object> value();
    int[] types();
    Complexity complexity();
}

非法示例

public @interface FailingAnnotation {
    Object complexity();  // ❌ 编译错误:Object 不是合法类型
}

2.5. 哪些程序元素可被注解?

注解可应用于多种声明位置:

  • 类/构造器/字段

    @SimpleAnnotation
    public class Apply {
        @SimpleAnnotation
        private String aField;
    
        @SimpleAnnotation
        public Apply() { ... }
    }
    
  • 方法及参数

    @SimpleAnnotation
    public void aMethod(@SimpleAnnotation String param) { ... }
    
  • 局部变量(含循环/资源变量)

    @SimpleAnnotation int i = 10;
    
    for (@SimpleAnnotation int j = 0; j < i; j++) { ... }
    
    try (@SimpleAnnotation FileWriter writer = getWriter()) {
        ...
    } catch (Exception ex) { ... }
    
  • 其他注解类型

    @SimpleAnnotation
    public @interface ComplexAnnotation { ... }
    
  • (通过 package-info.java

    @PackageAnnotation
    package com.example.annotations;
    

Java 8+ 新增类型注解(需指定 @Target(ElementType.TYPE_USE)):

// 类实例创建
new @SimpleAnnotation Apply();

// 类型转换
aString = (@SimpleAnnotation String) something;

// 泛型
public class SimpleList<T> 
  implements @SimpleAnnotation List<@SimpleAnnotation T> { ... }

// 异常声明
void aMethod() throws @SimpleAnnotation Exception { ... }

2.6. 如何限制注解的应用范围?

使用 @Target 元注解限定目标元素,越界使用会触发编译错误:

// 仅限字段
@Target(ElementType.FIELD)
public @interface SimpleAnnotation { ... }

// 多目标组合
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PACKAGE })

特殊场景:完全禁用注解(仅作为复杂注解的成员类型):

@Target({})
public @interface NoTargetAnnotation { ... }

2.7. 什么是元注解?

元注解是作用于其他注解的注解。满足以下条件即视为元注解:

  • 未声明 @Target,或
  • @Target 包含 ANNOTATION_TYPE 常量
@Target(ElementType.ANNOTATION_TYPE)
public @interface SimpleAnnotation { ... }

2.8. 什么是可重复注解?

允许在同一元素上多次应用的注解(Java 8+ 特性)。实现需两步:

  1. 声明可重复注解(指定容器注解)
    @Repeatable(Schedules.class)
    public @interface Schedule {
        String time() default "morning";
    }
    
  2. 定义容器注解(必须包含 value 元素)
    public @interface Schedules {
        Schedule[] value();
    }
    

使用示例

@Schedule
@Schedule(time = "afternoon")
@Schedule(time = "night")
void scheduledMethod() { ... }

2.9. 如何检索注解?与保留策略的关系?

通过 反射 API注解处理器 检索,受 @Retention 策略影响:

保留策略 存储位置 检索方式
SOURCE 仅源码 注解处理器(编译时)
CLASS Class 文件 字节码解析工具(非反射)
RUNTIME Class 文件 + JVM 反射 API(运行时)

运行时检索示例

@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
    String value();
}

// 反射获取注解
Description description = AnnotatedClass.class.getAnnotation(Description.class);
System.out.println(description.value());

2.10. 以下代码能编译吗?

@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.FIELD })
public @interface TestAnnotation {
    int[] value() default {};
}

编译失败@Target 中重复出现 ElementType.FIELD。修正如下:

@Target({ ElementType.FIELD, ElementType.TYPE })

2.11. 注解可以继承吗?

不能。根据 Java 语言规范,所有注解隐式继承 java.lang.annotation.Annotation

尝试显式继承会报错:

public @interface AnAnnotation extends OtherAnnotation { 
    // ❌ 编译错误:注解不能继承
}

3. 结论

本文梳理了 Java 注解的核心面试问题,涵盖基础概念、自定义注解、元注解、检索机制等关键点。虽然未穷尽所有细节,但足以作为深入研究的起点。祝您面试顺利!


原始标题:Java Annotations Interview Questions(+ Answers)