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,编译器校验重写逻辑@Overridepublic 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:是否打印执行时间,默认trueboolean 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("这个方法要被淘汰了");}// 新方法,替代oldMethodpublic 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 removedtool.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
)定规则,用反射写解析器,就能实现 “贴标签→自动执行逻辑”。