注解
注解(Annotation)可以理解为:写在代码上的“结构化标记”。它不像普通注释只给人看,注解是给 编译器 / 工具 / 框架看的元数据。
如果你是前端,可以把它类比成:
- TS 里的 装饰器(decorator):
@Component / @Injectable / @Controller ... - 或者“给代码贴标签”,让框架在启动时去扫描这些标签,然后做注册、校验、生成代码等工作
注解能干什么
按“发生在什么时候”来分更好记:
- 编译期:给编译器/IDE 提示(例如
@Override)、做静态检查(例如@SuppressWarnings) - 运行期:框架通过 反射读取注解,然后做“自动装配/路由注册/参数校验/鉴权”等(前提是
@Retention(RUNTIME))
常用的预定义注解(JDK 自带)
@Override
表示“我这个方法是重写父类/接口的方法”。好处是:写错方法签名会直接编译失败,避免你以为重写成功了,实际没有生效。
java
package com.tomiaa;
/**
* 示例代码(kuangyx.cn)
*
* @author : tomiaa
* @website : https://kuangyx.cn
* @project : kuangyx
* @package : com.tomiaa
* @className : OverrideTest
*/
public class OverrideTest {
private Integer id;
private String name;
public OverrideTest(Integer id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "OverrideTest{id=" + id + ", name='" + name + "'}";
}
public static void main(String[] args) {
OverrideTest demo = new OverrideTest(101, "tomiaa");
System.out.println(demo);
}
}@Deprecated
表示“这个类/方法/字段已经过时,不建议再用”。一般配合 Javadoc 说明替代方案:
java
package com.tomiaa;
/**
* 示例代码(kuangyx.cn)
*
* @author : tomiaa
* @website : https://kuangyx.cn
* @project : kuangyx
* @package : com.tomiaa
* @className : DeprecatedTest
*/
public class DeprecatedTest {
/**
* @deprecated 已废弃,请使用 newSayHello()
*/
@Deprecated
public static void sayHello() {
System.out.println("Hello World!");
}
public static void newSayHello() {
System.out.println("Hello, Welcome to kuangyx.cn!");
}
public static void main(String[] args) {
// IDE 一般会对 sayHello() 标黄提示
sayHello();
newSayHello();
}
}@SuppressWarnings
表示“我知道这里会有某种警告,但我确认它是安全的/可接受的,请不要提示我”。
常见值:
| 值 | 含义(你大概在哪会遇到) |
|---|---|
deprecation | 调用了 @Deprecated 的 API |
unchecked | 泛型擦除导致的未经检查转换(常见于集合 + 强转) |
rawtypes | 使用了原生类型(例如 List 而不是 List<String>) |
unused | 定义了变量/方法但未使用 |
all | 关闭以上所有警告(不推荐,范围太大) |
java
package com.tomiaa;
import java.util.ArrayList;
import java.util.List;
/**
* 示例代码(kuangyx.cn)
*
* @author : tomiaa
* @website : https://kuangyx.cn
* @project : kuangyx
* @package : com.tomiaa
* @className : SuppressWarningsTest
*/
public class SuppressWarningsTest {
@SuppressWarnings({"rawtypes", "unchecked"})
public static void main(String[] args) {
// rawtypes: 没写泛型参数
List list = new ArrayList();
// unchecked: 往 raw list 里塞 String
list.add("kuangyx.cn");
System.out.println(list);
}
}自定义注解(你也可以写自己的“标签”)
1)怎么定义
用 @interface: interface 是关键字
java
public @interface AnnoDemo {
String name() default "tomiaa";
int age() default 20;
float score() default 60.0f;
}2)注解的“参数”是什么
注解里看起来像“无参方法”的东西,其实就是注解的参数(也叫元素)。
规则记住三条就够用:
- 返回值不能是
void - 支持的类型有限:基本类型、
String、Class、枚举、注解、以及它们的数组 - 如果只有一个参数叫
value,使用时可以省略value=
java
public @interface Role {
String value();
}
@Role("admin") // 等价于 @Role(value = "admin")
class User {}3)什么时候“读得到”自定义注解
这取决于 @Retention(生命周期):
SOURCE:只在源码里,编译后就没了(运行时读不到)CLASS:在字节码里,但 JVM 运行时默认拿不到(大多数情况下读不到)RUNTIME:运行时保留,可以用反射读到(框架最常用)
元注解(修饰“注解的注解”)
- 元注解的作用是:告诉编译器/框架“你的注解能贴在哪、能保存到什么时候、能否继承、能否重复”等。
- 👉 @Target 是 Java 官方提供的「元注解」
- 👉 定义在 java.lang.annotation 包里
- 👉 用来限制“你的注解可以贴在哪些地方”
@Target:能贴在哪
常见取值:
| 值 | 说明 |
|---|---|
ElementType.TYPE | 类/接口/枚举 |
ElementType.FIELD | 成员变量 |
ElementType.METHOD | 方法 |
ElementType.PARAMETER | 方法参数 |
ElementType.CONSTRUCTOR | 构造方法 |
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // “这个注解,只允许贴在方法上”
public @interface TrackTime {}@Retention:保留到什么时候
java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {}@Documented:是否进入生成文档
java
import java.lang.annotation.Documented;
@Documented
public @interface TrackTime {}@Inherited:子类是否继承(只对类有效)
java
import java.lang.annotation.Inherited;
@Inherited
public @interface ApiModule {}@Repeatable:同一位置能否重复标注
java
import java.lang.annotation.Repeatable;
@Repeatable(Tags.class)
public @interface Tag {
String value();
}
public @interface Tags {
Tag[] value();
}
@Tag("backend")
@Tag("java")
class Post {}运行时读取注解:为什么框架离不开它
当一个注解是 RetentionPolicy.RUNTIME 时,框架就能通过反射读取它:
java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface Role {
String value();
}
@Role("admin")
class User {}
public class ReadAnnoDemo {
public static void main(String[] args) {
Role role = User.class.getAnnotation(Role.class);
System.out.println(role.value()); // admin
}
}你可以把这个过程想象成:框架在启动时扫描 class,然后根据注解里写的“配置”,自动把它注册进容器/路由表/校验链路里。
总结
- 注解 = 给编译器/框架看的标签(元数据)
- 想让框架运行时读到 → 记得
@Retention(RUNTIME) @Target决定能贴在哪;@Repeatable决定能不能贴多个;@Inherited影响类继承场景- JDK 常用三个:
@Override(防写错)、@Deprecated(提示替代)、@SuppressWarnings(我知道我在干啥)