全面解析java注解

作者:学到头秃的suhian日期:2025/10/11

一.注解的定义与分类

注解的概念:

Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法

JDK自带注解:

@Override 代表子类重写父类的方法

@Deprecated 代表该方法已经过时

@SuppressWarning 代表忽略警告Warnings

注解的分类:

  • 源码注解:注解只在源码中存在,编译成.class文件就不存在了
  • 编译时注解:注解在源码和.class文件中都存在(@Override,@Deprecated这些都是)
  • 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解(@Autowired)

注解的代价

凡事有得必有失,注解技术同样如此。使用注解也有一定的代价:

  • 显然,它是一种侵入式编程,那么,自然就存在着增加程序耦合度的问题。
  • 自定义注解的处理需要在运行时,通过反射技术来获取属性。如果注解所修饰的元素是类的非 public 成员,也可以通过反射获取。这就违背了面向对象的封装性。
  • 注解所产生的问题,相对而言,更难以 debug 或定位。

二.自定义注解

1.自定义注解的语法要求

a.使用**@interface**关键字定义注解

b.成员以无参无异常方式声明

c.可以用default为成员指定一个默认值

d.其中成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation,Enumeration

e.如果注解只有一个成员,则成员名必须为value(),在使用时可以忽略成员名和赋值号(=)

f.注解类可以没有成员,没有成员的注解称为标识注解(例如@Overrride)

1//以下成员都是不合法的,因为成员要无参数,无异常
2String desc(int a);
3String desc() throws Exception;
4
5//成员类型为受限的,以下不可以
6Map desc();
1//语法:
2//注解属性只能使用 public 或默认访问级别(即不指定访问级别修饰符)修饰。
3[访问级别修饰符] [数据类型] 名称() default 默认值;
4

元注解:

  • @Target是作用域

  • @Retention生命周期

  • @Inherited 这是一个标识型元注解,即允许继承(默认情况下不是这样)

  • @Documented 生成javadoc时会包含注解

2.使用自定义注解

解析注解:

概念:通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑

1public static void main(String[] args){
2  //1.使用类加载器加载类
3  try{
4    Class c=Class.forName("com.ann.test.Child");
5    //2.找到类上面的注解
6    boolean isExist=c.isAnnotation(Description.class);
7    if(isExist){
8      //3.拿到注解实例
9      Description d=(Description)c.getAnnotation(Description.class);
10      System.out.println(d.value());
11}
12    //4.找到方法上的注解
13    Method[] ms=c.getMethods();
14    for(Method m:ms){
15      boolean isExist=m.isAnnotation(Description.class);
16      if(isExist){
17         Description d=(Description)m.getAnnotation(Description.class);
18         System.out.println(d.value())}}
19}
20    
21}catch(ClassNotFoundException e){
22    e.printStackTrace();
23}
24}

三.注解实战

需求:

  1. 有一张用户表,字段包括用户ID,用户名,昵称,年纪,性别,所在城市,邮箱,手机号
  2. 方便的对每个字段或者字段的组合条件进行检索,并打印出SQL
1package reflectionandproxy.test;
2//1.首先考虑代码如何与数据库进行映射--->Filter和数据库字段相似
3@Table("user")
4public class Filter {
5    @Column("id")
6    private int id;
7
8    @Column("user_name")
9    private String userName;
10
11    @Column("nick_name")
12    private String nickName;
13
14    @Column("age")
15    private int age;
16
17    @Column("city")
18    private String city;
19
20    @Column("email")
21    private String email;
22
23    @Column("mobile")
24    private String mobile;
25
26    public String getMobile() {
27        return mobile;
28    }
29
30    public void setMobile(String mobile) {
31        this.mobile = mobile;
32    }
33
34    public int getId() {
35        return id;
36    }
37
38    public void setId(int id) {
39        this.id = id;
40    }
41
42    public String getUserName() {
43        return userName;
44    }
45
46    public void setUserName(String userName) {
47        this.userName = userName;
48    }
49
50    public String getNickName() {
51        return nickName;
52    }
53
54    public void setNickName(String nickName) {
55        this.nickName = nickName;
56    }
57
58    public int getAge() {
59        return age;
60    }
61
62    public void setAge(int age) {
63        this.age = age;
64    }
65
66    public String getCity() {
67        return city;
68    }
69
70    public void setCity(String city) {
71        this.city = city;
72    }
73
74    public String getEmail() {
75        return email;
76    }
77
78    public void setEmail(String email) {
79        this.email = email;
80    }
81}
82
1package reflectionandproxy.test;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8@Target({ElementType.FIELD})
9@Retention(RetentionPolicy.RUNTIME)
10public @interface Column {
11    String value();//数据库字段名
12}
1package reflectionandproxy.test;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8@Target({ElementType.TYPE})
9@Retention(RetentionPolicy.RUNTIME)
10
11public @interface Table {
12    String value();//表名
13}
1package reflectionandproxy.test;
2
3import java.lang.reflect.Field;
4import java.lang.reflect.InvocationTargetException;
5import java.lang.reflect.Method;
6
7public class Test {
8    public static void main(String[] args) {
9        Filter f1=new Filter();
10        f1.setId(10);//查询ID为10的用户
11
12        Filter f2=new Filter();
13        f2.setUserName("Tom");//模糊查询用户名为Tom的用户
14
15        Filter f3=new Filter();
16        f3.setEmail("[email protected],[email protected],[email protected]");//查询邮箱为其中任意一个
17
18        //查询sql语句
19        String sql1=query(f1);
20        String sql2=query(f2);
21        String sql3=query(f3);
22
23        //输出
24        System.out.println(sql1);
25        System.out.println(sql2);
26        System.out.println(sql3);
27    }
28
29    private static String query(Filter f1) {
30        //2.接下来我们就考虑如何实现query方法
31        StringBuilder sb=new StringBuilder();
32        //a.获取到class
33        Class clazz=f1.getClass();
34        //b.获取到table的名字
35        boolean exists = clazz.isAnnotationPresent(Table.class);
36        if(! exists){
37            return null;
38        }
39        Table table= (Table) clazz.getAnnotation(Table.class);
40        String tableName=table.value();
41        sb.append("select * from ").append(tableName).append(" where 1=1");
42
43        //c.获取到所有的字段
44        Field[] fields=clazz.getDeclaredFields();
45        for (Field field : fields){
46            //4.处理每个字段对应的sql语句
47            //4.1 获取到字段名
48            boolean exists1 = field.isAnnotationPresent(Column.class);
49            if(! exists1){
50                continue;
51            }
52            Column column=field.getAnnotation(Column.class);
53            String columnName=column.value();
54            //4.2 获取字段值
55            String fieldName = field.getName();
56            String getMethodName = "get" + fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
57            Object fieldValue=null;
58            try {
59                Method getMethod = clazz.getMethod(getMethodName);
60                fieldValue =getMethod.invoke(f1);
61            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
62                throw new RuntimeException(e);
63            }
64            //4.3拼装sql语句
65            if(fieldValue==null||(fieldValue instanceof Integer && ((Integer) fieldValue).intValue()==0)){
66                continue;
67            }
68            sb.append(" and ").append(columnName).append(" = ").append(fieldValue);
69        }
70        return sb.toString();
71
72    }
73}

控制台输出:


全面解析java注解》 是转载文章,点击查看原文


相关推荐


Python 的内建函数
hubenchang05152025/10/9

#Python 的内建函数 此文档创建于 Python 3.13,可能未及时更新,请以 Python 官方文档 为准。 虽然称为内建函数,但部分 API 并不是函数,例如 object 是类。 函数名详情简介__import__查看导入模块abs查看计算绝对值aiter查看获取异步可迭代对象的迭代器all查看判断可迭代对象内容是否全部为真值anext查看获取异步迭代器的下一数据项any查看判断可迭代对象内容是否存在真值ascii查看转换为字符串,非 ASCII 字符将被转义bin查看将一


如何使用 INFINI Gateway 对比 ES 索引数据
极限实验室2025/10/8

上一篇我们通过 极限网关(INFINI Gateway) 进行了索引数据迁移,对索引迁移结果进行了初步且直观的校验,既对比索引的文档数是否一致。今天介绍个实实在在的数据比对方法,通过网关对比索引文档的内容在两个集群是否一致,此方法适用于 Elasticsearch、Easysearch、Opensearch。话不多说,就拿上次迁移的两个索引开整。 测试环境 软件版本Easysearch1.12.0Elasticsearch7.17.29INFINI Ga


从0-1建设数据仓库
DashingGuy2025/10/7

基于onedata,纯个人理解,不完善的会慢慢补充 整体流程 业务调研数据调研划分数据域构建总线矩阵数仓模型设计数仓模型开发数仓模型质量保障以及运维 一、业务调研 业务调研有几个内容要做: 确定目标和范围、收集业务需求、梳理业务流程和数据流向、输出物 1.1 确定目标和范围 明确业务目标:为什么建设数仓?数仓要解决什么问题?要实现哪些业务目标?例如提升数据分析能力、提高经营效率、支持精准营销、预测风险等。 确定数仓范围:数仓要包含哪些业务领域?哪些数据需要纳入数仓?需要支持哪些业务场景?例


在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
IT橘子皮2025/10/5

在高并发场景下,平衡QPS(每秒查询数)和TPS(每秒事务数)的监控资源消耗,需要从监控策略、工具选型、资源分配及优化手段等多维度进行综合设计。以下是具体方案: 一、分层监控策略 ​轻量级采样与聚合​ ​高频QPS监控​:对简单请求(如API调用)采用低精度采样(如每10秒聚合一次),通过Prometheus的rate()函数计算QPS趋势,减少实时采集压力。 ​低频TPS监控​:对复杂事务(如支付流程)采用事件驱动记录,仅记录事务开始和结束时间戳,通过日志分析或消息队列(如Kafka)


深入理解 3D 火焰着色器:从 Shadertoy 到 Three.js 的完整实现解析
excel2025/10/4

本文由 AI 生成,结合 GLSL 原理与 Three.js 实践,旨在帮助初学者逐行理解代码,而不是仅仅“照抄能跑”。我会用直观类比、数值例子、代码注释来拆解整个火焰效果。 示例地址 一、前言:从 Shadertoy 到 Three.js Shadertoy 上有很多绚丽的着色器,但它们常常让新手望而生畏:几十行数学公式,cos/sin 嵌套,光线行进(raymarching)循环一堆看不懂的变量。 其实这些代码是有逻辑脉络的: 定义相机 → 每个像素对应一条射线 沿射线逐步前进(ray


Vue3和element plus在el-table中使用el-tree-select遇到的change事件坑
fxshy2025/10/3

1. 在el-tree-select中change事件使用 如果直接传递row, 拿到的不是最新的row的数据, 使用$index结合tableData来拿row <el-table-column prop="directory" label="所属目录树"> <template #default="scope"> <el-tree-select v-model="scope.row.areaTreeNodeId" :data="dir


贪心算法 | 每周8题(一)
小邓儿◑.◑2025/10/2

目录 0.0引言 1.0贪心算法介绍 1.1什么是贪心算法 2.0例题详解(来源力扣) 2.1 柠檬水找零 2.2将数组和减半的最少操作次数 2.3 最大数 2.4 K 次取反后最大化的数组和 2.5最长递增子序列 2.6摆动序列 2.7递增的三元子序列 2.8最长连续递增序列 3.0小结 0.0引言 从本周起,小编儿将带大家一起进入算法(^▽^)的学习当中。废话不多说,咱们从贪心走起儿😄😄😄 1.0贪心算法介绍 1.1什么是贪心算法 贪心算法(


风力发电机输出功率模型综述
less is more_09302025/10/2

摘要:在设计最优系统时,准确的建模是十分重要的。影响风机性能的主要因素是风速分布、轮毂高度以及所选风力机的功率输出曲线,故在进行风机建模时必须适当考虑这些因素。本文对风机输出功率建模的各种方法进行了详细的介绍。基于风能基本方程的建模方法使用时较繁琐,而且无法正确地复现实际风机的特性。基于假定功率曲线的建模方法虽然使用较为简单,但也缺乏准确性,不过当年平均风速较高时,表现出较为满意的响应。采用风机实际功率曲线建立特征方程的建模方法,当风机功率曲线光滑时,最小二乘法和三次样条插值法均能得到准确的结果


分布式光纤声波振动与AI的深度融合:开启智慧感知新时代
无锡布里渊10/2/2025

未来,随着AI算法的持续创新、算力的提升以及多源数据融合技术的发展,分布式光纤传感与AI的结合必将在更广阔的领域绽放异彩,为构建更安全、高效、智能的未来社会贡献核心力量。人工智能(AI),特别是机器学习与深度学习算法的迅猛发展,为解决这些难题提供了强大的工具,二者的深度融合正催生着智慧感知的新范式,前景广阔,优势显著。AI通过对大量标注数据的学习,能够精准识别不同物理事件(如不同类型的入侵行为、设备异常振动、管道泄漏等)的细微模式差异,大幅提高事件分类和识别的准确率,降低误报率和漏报率。


Word和WPS文字中的题注没有更新?全选按F9刷新
揭老师高效办公9/30/2025

在Word文档和WPS文字中有多个图片或表格等对象时,一个好的习惯是给这些图片和表格添加标题并编号,这些标题不要手动编号,要使用程序自带的“题注”功能,以实现自动更新序号。如果插入、删除或调整图片、表格后,题注的编号没有更新,可全选整个文档,按F9刷新实现自动编号。

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0