Java 注解入门:从认识 @Override 到写出第一个自定义注解
大家好!如果你在 Java 开发中见过@Override、@Controller这类带@的标记,却只知道 “贴上去能用”,说不出背后的逻辑 —— 那这篇入门博客正好适合你。今天我们从 “是什么”“怎么用” 两个角度,把注解的基础打牢,最后还会动手写一个能实际用的自定义注解,看完就能上手。
一、先搞懂:注解不是 “高级注释”,而是 “程序的标签”
第一次接触注解时,我总把它和注释搞混 —— 毕竟都带 “注” 字,而且看起来都是 “写在代码旁边的东西”。但后来踩了坑才明白,二者本质完全不同:
| 对比维度 | 注解(Annotation) | 注释(Comment) |
|---|---|---|
| 作用对象 | 给程序 / 框架 / 编译器看 | 给人看 |
| 处理方式 | 程序会解析并执行对应逻辑 | 编译器直接忽略 |
| 语法要求 | 严格遵循@注解名(属性)格式 |
随意写,// 或 /* */ 包裹就行 |
举个直观的例子:@Override注解贴在方法上,编译器会检查 “这个方法是不是真的重写了父类方法”—— 如果父类没有这个方法,直接爆红;而// 这是重写方法的注释,编译器只会当 “废话” 忽略。
所以,注解的核心是给代码贴 “机器能读懂的标签”,这些标签能告诉工具:“这段代码有特殊含义,你要这么处理它”。比如 Spring 看到@Controller,就知道 “这是个控制器,要把它注册成 Bean”;JUnit 看到@Test,就知道 “这是测试方法,要自动执行”。
二、Java 自带的 3 个基础注解:每天都在用,今天彻底懂
Java 从 1.5 就引入了注解,自带了 3 个最常用的基础注解,都在java.lang包下。平时写代码天天见,但很多人只知其然不知其所以然,我们逐个拆透。
1. @Override:重写方法的 “防错卫士”
作用:声明当前方法是 “重写父类 / 实现接口的方法”,核心价值是避免拼写错误。
你可能会问:“我不写这个注解,重写方法不也能跑吗?”—— 确实能跑,但容易踩坑。比如父类有个getUserName(),我想重写时不小心写成getUserNam()(少个 e):
- 没加
@Override:编译器会把它当成 “新方法”,不报错,但运行时调用的还是父类方法,排查半天找不到问题; - 加了
@Override:编译器会立刻爆红,提示 “父类没有这个方法”,直接把错误扼杀在编译阶段。
正确用法:只要重写方法,就贴在方法上,不用加任何属性:
class Parent {
public String getUserName() {
return "parent";
}
}
class Child extends Parent {
// 正确:贴@Override,编译器校验重写逻辑
@Override
public String getUserName() {
return "child";
}
// 错误:父类没有getUserNam(),加@Override会爆红
// @Override
// public String getUserNam() { return "error"; }
}
2. @Deprecated:标记 “过时方法” 的 “警示牌”
作用:告诉其他开发者 “这个方法 / 类 / 字段已经过时,别用了,后续版本会删”,避免大家用淘汰的 API 写代码。
比如 Java 8 里Date类的toLocaleString()方法,就被标记为过时 —— 如果你调用它,IDE 会弹出黄色警告,提示 “用DateTimeFormatter替代”。
正确用法:贴在要标记的元素上,还能加两个实用属性:
public class Test {
public static void main(String[] args) {
// 临时屏蔽“调用过时方法”的警告
@SuppressWarnings("deprecation")
OldTool tool = new OldTool();
tool.oldMethod(); // 警告消失
// 屏蔽“变量未使用”的警告
@SuppressWarnings("unused")
String temp = "临时变量,暂时不用";
}
}
三、注解的 “规则制定者”:元注解
前面的@Override、@Deprecated是 “普通注解”,而元注解是 “给注解贴的注解”—— 简单说,元注解用来定义 “一个注解能贴在哪”“能活多久” 这些规则。
Java 自带 5 个元注解,都在java.lang.annotation包下,掌握它们是写自定义注解的前提。我们用一个 “自定义注解模板” 来理解:
1. @Target:规定 “注解能贴在哪”
@Target的参数是ElementType枚举,用来限制注解的 “作用范围”,比如有的注解只能贴方法,有的能贴类。常见枚举值:
比如@Override的@Target是METHOD,所以你把它贴在类上,编译器会直接报错 —— 这就是@Target的 “规则约束” 作用。
2. @Retention:规定 “注解能活多久”
@Retention的参数是RetentionPolicy枚举,决定注解在 “源码→编译→运行” 三个阶段中,能保留到哪个阶段(活多久):
public class UserService {
// 贴注解:指定描述,默认打印时间
@LogMethod(desc = "用户登录验证(模拟耗时300ms)")
public void login() {
try {
Thread.sleep(300); // 模拟业务耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("登录成功:用户名=admin,密码=***");
}
// 贴注解:指定描述,不打印时间
@LogMethod(desc = "用户退出登录", printTime = false)
public void logout() {
System.out.println("退出成功:清除用户Session");
}
// 没贴注解
public void queryUser() {
System.out.println("查询用户:id=1");
}
// 测试入口
public static void main(String[] args) throws Exception {
LogUtils.execute(UserService.class, "login");
LogUtils.execute(UserService.class, "logout");
LogUtils.execute(UserService.class, "queryUser");
}
}
运行结果(符合预期)
plaintext
开始执行:用户登录验证(模拟耗时300ms)
登录成功:用户名=admin,密码=***
执行结束:耗时301ms
开始执行:用户退出登录
退出成功:清除用户Session
执行结束
查询用户:id=1
方法queryUser未加@LogMethod,不打印日志
四、动手实战:写一个能 “记录方法执行日志” 的自定义注解
光说不练假把式,我们来写一个实用的自定义注解@LogMethod,作用是 “自动记录方法的执行时间和描述”,步骤分 3 步:定义注解→写解析器→测试。
步骤 1:定义 @LogMethod 注解(用元注解定规则)
import java.lang.annotation.*;
// 元注解:1.只能贴在方法上;2.保留到运行时(反射要用);3.生成文档包含
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogMethod {
// 注解属性1:方法描述,默认空字符串
String desc() default "";
// 注解属性2:是否打印执行时间,默认true
boolean printTime() default true;
}
步骤 2:写注解解析器(用反射实现逻辑)
注解本身不会执行任何代码,必须写 “解析器”—— 用反射获取方法上的@LogMethod注解,然后在方法执行前后做日志记录。
步骤 3:测试注解效果
写一个UserService类,给方法贴@LogMethod,然后用LogUtils执行:
since:说明 “从哪个版本开始过时”;forRemoval:说明 “未来会不会删除”(true = 会删,IDE 警告更严重)。public class OldTool { // 标记:从1.2版本开始过时,未来会删除 @Deprecated(since = "1.2", forRemoval = true) public void oldMethod() { System.out.println("这个方法要被淘汰了"); } // 新方法,替代oldMethod public void newMethod() { System.out.println("用我替代旧方法!"); } } // 调用时:IDE会给oldMethod()标黄警告 public class Test { public static void main(String[] args) { OldTool tool = new OldTool(); tool.oldMethod(); // 警告:'oldMethod()' is deprecated since version 1.2 and will be removed tool.newMethod(); // 无警告 } }3. @SuppressWarnings:临时 “屏蔽警告” 的 “开关”
作用:屏蔽 IDE 或编译器弹出的 “已知但暂时无法解决的警告”,比如 “调用过时方法”“变量未使用”“未泛型化的集合”。
比如前面调用
oldMethod()时,我们知道它过时,但暂时没来得及替换,就可以用@SuppressWarnings让警告消失 —— 但注意:这是 “临时方案”,别滥用!正确用法:需要传一个 “警告类型” 参数,告诉它要屏蔽哪种警告,常见参数:
deprecation:屏蔽 “调用过时方法” 警告;unused:屏蔽 “变量 / 方法未使用” 警告;rawtypes:屏蔽 “集合未加泛型” 警告(如List list = new ArrayList());all:屏蔽所有警告(不推荐,容易忽略重要问题)。TYPE:类、接口、枚举;METHOD:方法;FIELD:成员变量(字段);PARAMETER:方法参数;CONSTRUCTOR:构造方法。@Override的@Retention是SOURCE:编译器用它校验完重写逻辑,就把它删了,class文件里看不到;- Spring 的
@Controller是RUNTIME:Spring 需要在运行时扫描这些注解,把类注册成控制器,所以必须保留到运行时。
五、入门总结:注解入门就这 3 件事
到这里,注解的入门知识就讲完了。下次写代码时,别再只当@Override是 “多余的标记”—— 它是你的 “防错卫士”;也别再随意调用@Deprecated的方法 —— 它是框架给你的 “善意提醒”。下一篇我们会讲注解的进阶内容:底层原理、框架中的应用(比如 Spring 的@Autowired),以及复杂场景的自定义注解,敬请期待!
- 分清注解和注释:注解是给程序看的,会被解析执行;注释是给人看的,会被忽略;
- 掌握 3 个基础注解:
@Override防拼写错,@Deprecated标过时,@SuppressWarnings临时屏蔽警告; - 会写简单自定义注解:用元注解(
@Target+@Retention)定规则,用反射写解析器,就能实现 “贴标签→自动执行逻辑”。
更多推荐


所有评论(0)