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

JAVA中的注解和泛型

目录

JAVA注解介绍

概念

注解的本质

4种标准元注解

自定义注解

泛型介绍

泛型的定义

JAVA泛型

泛型方法( )

泛型类( )

类型通配符

类型擦除


JAVA注解介绍

概念

注解是 JDK 5.0 引入的一种元数据机制,用来对代码进行标注。它不会影响程序的实际逻辑,但可被编译器、开发工具、框架或运行时反射机制读取,用于生成代码、配置信息、提供指令等。

简单理解:注解就是“给代码贴标签”,再由工具读取标签做相应处理。

注解的本质

Java 中的注解是一个接口,所有注解都是 java.lang.annotation.Annotation 接口的子类型。注解可以添加在类、方法、变量、参数等元素上。

我们可以通过反射获取注解的信息:

Class<?> clazz = MyClass.class;
if (clazz.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);System.out.println(annotation.value());
}

4种标准元注解

元注解的作用是负责注解其他注解。 Java5.0定义了 4个标准的 meta-annotation类型,它们被用来提供对其它 annotation类型作说明。

@Target —— 说明注解可以应用于哪些 Java 成员上

@Target({ElementType.TYPE, ElementType.METHOD})

常见取值(ElementType):

  • TYPE: 类、接口、枚举

  • FIELD: 成员变量

  • METHOD: 方法

  • PARAMETER: 参数

  • CONSTRUCTOR: 构造方法

  • LOCAL_VARIABLE: 局部变量

  • ANNOTATION_TYPE: 注解

  • PACKAGE: 包

@Retention —— 指定注解的生命周期

@Retention(RetentionPolicy.RUNTIME)

常见取值(RetentionPolicy):

  • SOURCE:仅在源码中保留,编译后丢弃(如@Override

  • CLASS:编译时保留,运行时不可通过反射获取(默认值)

  • RUNTIME:运行时仍可读取(用于框架反射,如 Spring、MyBatis)

@Documented —— 表示注解是否包含在 Javadoc 中

@Documented

@Inherited —— 注解是否可以被子类继承

@Inherited #仅对类有效:如果某个类使用了被 @Inherited 修饰的注解,那么其子类也会继承这个注解。

自定义注解

/** 需要登录才可以访问对应的资源*/
@Target(ElementType.METHOD) // 注解作用于方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时仍然可用
@Documented
public @interface RequireLogin {String role() default "user";
}
public class AnnotationTest {
​@RequireLogin(role = "admin")public void deleteUser() {System.out.println("删除用户");}
​@RequireLoginpublic void queryUser() {System.out.println("查询用户");}
​public void publicMethod() {System.out.println("公共方法,不需要登录");}
​public static void main(String[] args) {//获取注解的反射信息AnnotationTest test = new AnnotationTest();// 获取类的注解Class<?> clazz = AnnotationTest.class;Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(RequireLogin.class)) {RequireLogin annotation = method.getAnnotation(RequireLogin.class);System.out.println("方法: " + method.getName() + ", 角色: " + annotation.role());}}}
}

注解一般需要配置拦截器使用实现对应的业务场景,例如日志、权限等功能实现,也可以配合AOP使用来实现一些特定的场景需求

以下例子是我自己写的一个实现登录日志记录的案例:

注解

/*** 自定义操作日志注解* @author hyh*/
@Target({ElementType.METHOD}) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface LoginLog {String businessName() default ""; // 操作名称
}

AOP切面

这里对应数据库操作的类换成自己的

/*** 登录日志记录切面** @author hyh* @date 2024-06-012*/
@Aspect
@Component
public class LoginLogAspect {@Autowiredprivate HttpServletRequest request;
​@Autowiredprivate ISysLoginInforService sysLoginInforService;
​// 切点定义 只要包含 @LoginLog 注解的方法都会被拦截@Pointcut("@annotation(com.hyh.ad.common.annotation.LoginLog)")public void operationLogPointCut() {}
​// 方法正常返回之后执行 afterReturningLogWrite 方法。@AfterReturning(pointcut = "operationLogPointCut()", returning = "returnValue")public void afterReturningLogWrite(JoinPoint joinPoint, Object returnValue)  {handleLog(joinPoint, null, returnValue);}
​//表示:方法抛出异常后执行 afterThrowingLogWrite 方法。@AfterThrowing(pointcut = "operationLogPointCut()", throwing = "e")public void afterThrowingLogWrite(JoinPoint joinPoint, Throwable e)  {handleLog(joinPoint, e, null);}
​//这个方法负责构建一个 SysLogininfor 登录日志对象,然后保存到数据库。private void handleLog(JoinPoint joinPoint, Throwable e, Object returnValue)  {// 创建日志对象SysLogininfor sysLogininfor = new SysLogininfor();MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();LoginLog log = method.getAnnotation(LoginLog.class);String username = "";
​Object[] args = joinPoint.getArgs();
​ObjectMapper objectMapper = new ObjectMapper();String params = "";try {params = objectMapper.writeValueAsString(args);} catch (JsonProcessingException jsonProcessingException) {jsonProcessingException.printStackTrace();}
​// 获取User-AgentString userAgentString = request.getHeader("User-Agent");UserAgent userAgent = UserAgent.parseUserAgentString(userAgentString);
​// 获取操作系统和浏览器信息OperatingSystem os = userAgent.getOperatingSystem();Browser browser = userAgent.getBrowser();
​// 获取参数里面的用户名if (args.length > 0 && args[0] instanceof LoginBody) {LoginBody loginBody = (LoginBody) args[0];username = loginBody.getUsername();} else {username = "Unknown";}String ipAddress = IpUtil.getIpAddr(request);sysLogininfor.setUserName(username);sysLogininfor.setIpaddr(ipAddress);sysLogininfor.setLoginLocation("内网IP");  // 或者通过IP地址来判断内外网sysLogininfor.setOs(os.getName());sysLogininfor.setBrowser(browser.getName());//时间加上8小时sysLogininfor.setLoginTime(LocalDateTime.now().plusHours(8));
​// 设置状态if (e != null) {sysLogininfor.setStatus("1");sysLogininfor.setMsg(e.getMessage()); // 设置错误信息} else {sysLogininfor.setStatus("0");if (returnValue != null) {String returnValueString = returnValue.toString(); // 假设 returnValue 是包含 {msg=验证码已过期, code=201} 的对象String msg = "";// 使用正则表达式提取 msg 字段Pattern pattern = Pattern.compile("msg=([^,}]*)");Matcher matcher = pattern.matcher(returnValueString);if (matcher.find()) {msg = matcher.group(1);}// 设置 sysLogininfor 的 msg 字段sysLogininfor.setMsg(msg);} else {sysLogininfor.setMsg("Operation completed successfully.");}}try {sysLoginInforService.insert(sysLogininfor);} catch (Exception exception) {exception.printStackTrace();}
}

应用的方法

/*** 用户名密码登录* @param loginBody* 对象参数包括以下字段:* 用户名 名字或者手机号* 密码* 验证码* uuid*/@ApiOperation(value = "通过用户名密码登陆 ")@PostMapping("/loginByPwd")@LoginLog(businessName = "用户登录") //注解public AjaxResult loginByPassword(@RequestBody LoginBody loginBody) {return sysUserService.loginByPassword(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid());}

泛型介绍

泛型的定义

泛型是 Java 中的一种语法机制,用于在类、接口和方法中参数化类型,让你在写代码时不用指定具体的数据类型,而在使用时再指定,从而提高代码的复用性类型安全性可读性

JAVA泛型

泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。比如我们要写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,我们就可以使用 Java 泛型。

泛型方法(<E>)

我们可以编写一个泛型方法,该方法在调用的时候可以接受不同的参数,根据传递的参数不同,编译器适当的处理每一个不同的调度

/** 编写泛型方法*/
public class FanxingMethodTest {//通用泛型方法public static <E> void printArray(E[] inputArray) {for (E e : inputArray) {System.out.println("元素的类型是: " + e.getClass().getName() +", 值: " + e);}}public static void main(String[] args) {printArray(new Integer[]{1, 2, 3, 4, 5});printArray(new String[]{"Hello", "World", "Generics"});printArray(new Double[]{1.1, 2.2, 3.3, 4.4, 5.5});printArray(new Character[]{'A', 'B', 'C', 'D', 'E'});}
}

泛型类(<T>)

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

泛型类

/** 泛型类*/
@Data
public class Box<T> {private T data;
}

泛型类测试

public class BoxTest {
​public static void main(String[] args) {//字符串类型Box<String> stringBox = new Box<String>();stringBox.setData("Hello, Generics!");System.out.println("String Box contains: " + stringBox.getData());
​//整数类型Box<Integer> integerBox = new Box<Integer>();integerBox.setData(123);System.out.println("Integer Box contains: " + integerBox.getData());}
}

类型通配符

类型通配符一般是使用 ?代替具体的类型参数。例如 List 在逻辑上是List,List 等所有 List<具体类型实参>的父类。

类型擦除

Java中的泛型基本上都是在编译器这个层次来实现的。在生成的 Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的 List和 List等类型,在编译之后都会变成 List,JVM看到的只是 List,而由泛型附加的类型信息对 JVM来说是不可见的。类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般是 Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。

  • 运行时无法获取泛型类型

  • List<String> list1 = new ArrayList<>();
    List<Integer> list2 = new ArrayList<>();
    ​System.out.println(list1.getClass() == list2.getClass()); // true
    ​
  • 无法使用 T 创建数组

  • public class Box<T> {T[] array = new T[10]; // ❌ 编译报错
    }
  • 不能用 instanceof 判断泛型类型

  • if (obj instanceof List<String>) {  // ❌ 编译错误
    }

相关文章:

JAVA中的注解和泛型

目录 JAVA注解介绍 概念 注解的本质 4种标准元注解 自定义注解 泛型介绍 泛型的定义 JAVA泛型 泛型方法( ) 泛型类&#xff08; &#xff09; 类型通配符 类型擦除 JAVA注解介绍 概念 注解是 JDK 5.0 引入的一种元数据机制&#xff0c;用来对代码进行标注。它不会影…...

Cesium快速入门到精通系列教程二:添加地形与添加自定义地形、相机控制

一、添加地形与添加自定义地形 在 Cesium 1.93 中添加地形可以通过配置terrainProvider实现。Cesium 支持多种地形数据源&#xff0c;包括 Cesium Ion 提供的全球地形、自定义地形服务以及开源地形数据。下面介绍几种常见的添加地形的方法&#xff1a; 使用 Cesium Ion 全球地…...

汽车零配件---ecu开发工厂学习

ecu成品制作工艺流程 一、PCB 设计与制作&#xff08;打板&#xff09; 工艺流程步骤 需求分析与电路设计 根据 ECU 功能&#xff08;如发动机控制、变速箱控制&#xff09;确定所需芯片&#xff08;如 MCU、传感器接口芯片&#xff09;、外围电路&#xff08;如电源、通信接…...

python学习打卡day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 浙大疏锦行 数据集使用猫狗数据集&#xff0c;训练集中包含猫图像4000张、狗图像4005张。测试集包含猫图像1012张&#xff0c;狗图像1013张。以下是数据集的下…...

Microsoft Word使用技巧分享(本科毕业论文版)

小铃铛最近终于完成了毕业答辩后空闲下来了&#xff0c;但是由于学校没有给出准确地参考模板&#xff0c;相信诸位朋友们也在调整排版时感到头疼&#xff0c;接下来小铃铛就自己使用到的一些排版技巧分享给大家。 注&#xff1a;以下某些设置是根据哈尔滨工业大学&#xff08;威…...

windows安装多个版本composer

一、需求场景 公司存在多个项目&#xff0c;有的项目比较老&#xff0c;需要composer 1.X版本才能使用 新的项目又需要composer 2.X版本才能使用 所以需要同时安装多个版本的composer二、下载多个版本composer #composer官网 https://getcomposer.org/download/三、放到指定目…...

【办公类-22-05】20250601Python模拟点击鼠标上传CSDN12篇

、 背景需求: 每周为了获取流量券,每天上传2篇,获得1500流量券,每周共上传12篇,才能获得3000和500的券。之前我用UIBOT模拟上传12篇。 【办公类-22-04】20240418 UIBOT模拟上传每天两篇,获取流量券,并删除内容_csdn 每日任务流量券-CSDN博客文章浏览阅读863次,点赞18…...

贪心算法应用:边着色问题详解

贪心算法应用&#xff1a;边着色问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。边着色问题是图论中的一个经典问题&#xff0c;贪心算法可以有效地解决它。下面我将从基础概念到具体实现&#xff0c;全…...

【蓝桥杯】包子凑数

包子凑数 题目描述 小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 NN 种蒸笼&#xff0c;其中第 ii 种蒸笼恰好能放 AiAi​ 个包子。每种蒸笼都有非常多笼&#xff0c;可以认为是无限笼。 每当有顾客想买 XX 个包子&#xff0c;卖包子的大叔就会迅速选出若干…...

ck-editor5的研究 (2):对 CKEditor5 进行设计,并封装成一个可用的 vue 组件

前言 在上一篇文章中—— ck-editor5的研究&#xff08;1&#xff09;&#xff1a;快速把 CKEditor5 集成到 nuxt 中 &#xff0c;我仅仅是把 ckeditor5 引入到了 nuxt 中&#xff0c;功能还不算通用。 这一篇内容将会对其进行设计&#xff0c;并封装成可复用的 vue 组件&…...

Java-redis实现限时在线秒杀功能

1.使用redisson pom文件添加redisson <!--redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.4</version></dependency> 2.mysql数据库表设…...

simulink mask、sfunction和tlc的联动、接口

这里全部是讲的level2 sfunction&#xff08;用m语言编写&#xff09;&#xff0c;基于matlab 2020a。 1.mask的参数操作 1&#xff09;mask通过set_param和get_param这2个函数接口对mask里面定义的Parameters&Dialog的参数的大部分属性进行读写&#xff0c;一般是Value值…...

VMWare安装常见问题

如果之前安装过VMWare软件&#xff0c;只要是 15/16 版本的&#xff0c;可以正常使用的&#xff0c;不用卸载&#xff01;&#xff01;&#xff01; 如果之前安装过&#xff0c;卸载了&#xff0c;一定要保证通过正常的渠道去卸载&#xff08;通过控制面板卸载软件&#xff09…...

set_property LOC约束

##下列指令是用于清除自带GT CELL相关的LOC约束&#xff0c;或者覆盖 ##你需要把IP中自带的GT cell相关的LOC约束清除掉&#xff0c;或者覆盖掉 ##以下命令可以用来覆盖GT_CHANNEL的LOC约束, 在这条命令之后执行你自己的physical constraint: ##GT的channel的相关管脚有两种设计…...

【北邮 操作系统】第十二章 文件系统实现

一、文件的物理结构 1.1 文件块、磁盘块 类似于内存分页&#xff0c;磁盘中的存储单元也会被分为一个个“块/磁盘块/物理块”。很多操作系统中&#xff0c;磁盘块的大小与内存块、页面的大小相同 内存与磁盘之间的数据交换(即读/写操作、磁盘I/0)都是以“块”为单位进行的。即…...

Docker 插件生态:从网络插件到存储插件的扩展能力解析

Docker 容器技术以其轻量、快速、可移植的特性,迅速成为构建和部署现代应用的核心工具。然而,尽管 Docker Engine 自身功能强大,但在面对多样化的生产环境和复杂业务需求时,仅靠核心功能往往无法满足所有场景。 例如,跨主机的容器网络通信、异构存储系统的持久化数据管理…...

WordPress搜索引擎优化的最佳重定向插件:进阶指南

在管理网站时&#xff0c;我们经常需要调整网页地址或修复错误链接。这时&#xff0c;通过重定向不仅能有效解决这些问题&#xff0c;还能显著提升网站在搜索引擎中的排名。对于熟悉基础重定向插件的用户来说&#xff0c;一些功能更强大的工具可以帮助你更全面地管理网站&#…...

org.junit.runners.model.InvalidTestClassError:此类问题的解决

不知道大家是否遇见过以上这种情况&#xff0c;我也是今天被这个错误搞得很烦&#xff0c;后来通过网上查找资料终于找到了问题所在————就是简单的Test注解的错误使用 Test注解的注意情况 &#xff1a;1 权限必须是public 2 不能有参数 3 返回值类型是void 4 本类的其他的…...

用户管理页面(解决toggleRowSelection在dialog用不了的隐患,包含el-table的plus版本的组件)

新增/编辑/删除/分配角色&#xff0c;图片上传在此文章分类下另一个文章 1.重点分配角色&#xff1a; <template><!-- 客户资料 --><div class"pageBox"><elPlusTable :tableData"tableData" :tablePage"tablePage" onSi…...

打卡第35天:GPU训练以及类的Call方法

知识点回归&#xff1a; 1.CPU性能的查看&#xff1a;看架构代际、核心数、线程数 2.GPU性能的查看&#xff1a;看显存、看级别、看架构代际 3.GPU训练的方法&#xff1a;数据和模型移动到GPU device上 4.类的call方法&#xff1a;为什么定义前向传播时可以直接写作self.fc1(x)…...

Linux-GCC、makefile、GDB

GCC gcc -E test.c -o test.i预处理(-o指定文件名) gcc -S test.i -o test.s编译gcc -c test.s -o test.o汇编gcc test.o -o test链接(生成一个可执行程序的软连接) gcc test.c -o test一条指令可以完成以上所有内容 gcc *.c -I(大写的i) include由于在main.c中找不到当前文件…...

[MySQL初阶]MySQL(7) 表的内外连接

标题&#xff1a;[MySQL初阶]MySQL(7)表的内外连接 水墨不写bug 文章目录 一. 内连接 (INNER JOIN)二. 外连接 (OUTER JOIN)关键区别总结 三、 如何选择 在 MySQL 中&#xff0c;连接&#xff08;JOIN&#xff09;用于根据两个或多个表之间的相关列组合行。内连接&#xff08;I…...

Spring Boot中Excel处理完全指南:从基础到高级实践

Excel处理基础知识 1.1 为什么需要在应用中处理Excel文件&#xff1f; 在企业应用开发中&#xff0c;Excel文件处理是一个非常常见的需求&#xff0c;主要用于以下场景&#xff1a; 数据导入&#xff1a;允许用户通过Excel上传批量数据到系统 数据导出&#xff1a;将系统数据…...

Windows下NVM的安装与使用

本文将介绍windows下nvm相关知识。 在不同的项目中可能会使用不同版本的Node.js&#xff0c;例如A项目中需要node>18&#xff1b;B项目中需要node>20。这时候就需要使用NVM切换不同的node版本。进而可以在同一台设备上使用多个node版本。 一、NVM是什么&#xff1f; n…...

Ubuntu挂起和休眠

Ubuntu挂起和休眠 1. 挂起&#xff08;Suspend&#xff09;2. 休眠&#xff08;Hibernate&#xff09;3. 混合挂起&#xff08;Hybrid-Sleep&#xff09;注意事项图形界面操作 在 Ubuntu 系统中&#xff0c;挂起&#xff08;Suspend&#xff09;和休眠&#xff08;Hibernate&am…...

【R语言编程绘图-mlbench】

mlbench库简介 mlbench是一个用于机器学习的R语言扩展包&#xff0c;主要用于提供经典的基准数据集和工具&#xff0c;常用于算法测试、教学演示或研究场景。该库包含多个知名数据集&#xff0c;涵盖分类、回归、聚类等任务。 包含的主要数据集 BostonHousing 波士顿房价数据…...

云服务器部署Gin+gorm 项目 demo

更多个人笔记见&#xff1a; &#xff08;注意点击“继续”&#xff0c;而不是“发现新项目”&#xff09; github个人笔记仓库 https://github.com/ZHLOVEYY/IT_note gitee 个人笔记仓库 https://gitee.com/harryhack/it_note 个人学习&#xff0c;学习过程中还会不断补充&…...

MySQL数据一致性守护者:pt-table-checksum原理与实战全解析

MySQL数据一致性守护者:pt-table-checksum原理与实战全解析 在MySQL主从复制环境中,数据一致性是DBA和运维人员最关心的问题之一。主从数据不一致可能导致业务逻辑错误、报表数据失真甚至系统故障。Percona Toolkit中的pt-table-checksum工具正是为解决这一痛点而生,它能够…...

检索器组件深入学习与使用技巧 BaseRetriever 检索器基类

1. BaseRetriever 检索器基类 在 LangChain 中&#xff0c;传递一段 query 并返回与这段文本相关联文档的组件被称为 检索器&#xff0c;并且 LangChain 为所有检索器设计了一个基类——BaseRetriever&#xff0c;该类继承了 RunnableSerializable&#xff0c;所以该类是一个 …...

Unity——QFramework工具 AciontKit时序动作执行系统

AciontKit 是一个时序动作执行系统。 游戏中&#xff0c;动画的播放、延时、资源的异步加载、网络请求等&#xff0c;这些全部都是时序任务&#xff0c;而 ActionKit&#xff0c;可以把这些任务全部整合在一起&#xff0c;使用统一的 API&#xff0c;来对他们的执行进行计划。…...