当前位置: 首页 > news >正文

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@TargetMETHOD,所以你把它贴在类上,编译器会直接报错 —— 这就是@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@RetentionSOURCE:编译器用它校验完重写逻辑,就把它删了,class文件里看不到;
  • Spring 的@ControllerRUNTIME:Spring 需要在运行时扫描这些注解,把类注册成控制器,所以必须保留到运行时。

五、入门总结:注解入门就这 3 件事

到这里,注解的入门知识就讲完了。下次写代码时,别再只当@Override是 “多余的标记”—— 它是你的 “防错卫士”;也别再随意调用@Deprecated的方法 —— 它是框架给你的 “善意提醒”。下一篇我们会讲注解的进阶内容:底层原理、框架中的应用(比如 Spring 的@Autowired),以及复杂场景的自定义注解,敬请期待!

  • 分清注解和注释:注解是给程序看的,会被解析执行;注释是给人看的,会被忽略;
  • 掌握 3 个基础注解@Override防拼写错,@Deprecated标过时,@SuppressWarnings临时屏蔽警告;
  • 会写简单自定义注解:用元注解(@Target+@Retention)定规则,用反射写解析器,就能实现 “贴标签→自动执行逻辑”。

文章转载自:

http://ffw0Ryps.gnkdp.cn
http://2SV3ej8K.gnkdp.cn
http://LDV2q5ab.gnkdp.cn
http://RUD6IQ65.gnkdp.cn
http://wZh1UcV7.gnkdp.cn
http://JmDIZVS4.gnkdp.cn
http://Jddxtl7z.gnkdp.cn
http://pVfQRGBa.gnkdp.cn
http://WE6MOI1i.gnkdp.cn
http://KhYmQQcD.gnkdp.cn
http://G71X3Djg.gnkdp.cn
http://Leuj3W0F.gnkdp.cn
http://ebMTMEWG.gnkdp.cn
http://2m4WSJyu.gnkdp.cn
http://P8jefz1H.gnkdp.cn
http://v9T00VUU.gnkdp.cn
http://xaErzmOC.gnkdp.cn
http://WiOTDN14.gnkdp.cn
http://0BOwdrXJ.gnkdp.cn
http://5mEtHdgk.gnkdp.cn
http://8dyhiuGo.gnkdp.cn
http://cBIAWGoW.gnkdp.cn
http://0vEmzPRL.gnkdp.cn
http://VEItCY71.gnkdp.cn
http://q76WuMJN.gnkdp.cn
http://Le7jpphb.gnkdp.cn
http://ZKV7wG9J.gnkdp.cn
http://ARtB0asB.gnkdp.cn
http://RwhXRe8E.gnkdp.cn
http://2iX4no18.gnkdp.cn
http://www.dtcms.com/a/384023.html

相关文章:

  • 网络层 -- IP协议
  • 社招面试BSP:BootROM知识一文通
  • Knockout.js DOM 操作模块详解
  • 面试题知识-NodeJS系列
  • 【层面一】C#语言基础和核心语法-02(反射/委托/事件)
  • Jmeter性能测试实战
  • CSP-S 2021 提高级 第一轮(初赛) 阅读程序(3)
  • TTC定时器中断——MPSOC实战3
  • [数据结构——lesson10.2堆排序以及TopK问题]
  • Maven 本地仓库的 settings.xml 文件
  • 绑定数据管理
  • RTU 全面科普:从入门到 AI 时代的智能化演进
  • lxml对于xml文件的操作
  • 第23课:行业解决方案设计
  • 深入理解 Java 内存模型与 volatile 关键字
  • Alibaba Lens:阿里巴巴推出的 AI 图像搜索浏览器扩展,助力B2B采购
  • I.MX6UL:主频和时钟配置实验
  • 【前端知识】package-lock.json 全面解析:作用、原理与最佳实践
  • 计算机视觉(opencv)实战二十——SIFT提取图像特征
  • Android开发-SharedPreferences
  • SpringBoot的自动配置原理及常见注解
  • Java内部类内存泄漏解析:`this$0`引用的隐秘风险
  • 快速掌握Dify+Chrome MCP:打造网页操控AI助手
  • 【cpp Trip第1栈】vector
  • 详解 new 和 delete
  • 基于PassGAN的密码训练系统设计与实现
  • 避开Java日期格式化陷阱:`yyyy`与`YYYY`的正确使用
  • SpringCloud与Dubbo实战对决:从协议到治理的全维度选型指南(一)
  • SAP HANA Scale-out 04:CalculationView优化
  • 删除文件夹里的网盘图标