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

详解Java8中如何通过方法引用获取属性名/::的使用

在我们开发过程中常常有一个需求,就是要知道实体类中Getter方法对应的属性名称(Field Name),例如实体类属性到数据库字段的映射,我们常常是硬编码指定 属性名,这种硬编码有两个缺点。
1、编码效率低:因为要硬编码写属性名,很可能写错,需要非常小心,时间浪费在了不必要的检查上。
2、容易让开发人员踩坑:例如有一天发现实体类中Field Name定义的不够明确,希望换一个Field Name,那么代码所有硬编码的Field Name都要跟着变更,对于未并更的地方,是无法在编译期发现的。只要有未变更的地方都可能导致bug的出现。
而使用了方法引用后,如果Field Name变更及其对应的Getter/Setter方法变更,编译器便可以实时的帮助我们检查变更的代码,在编译器给出错误信息。

那么如何通过方法引用获取Getter方法对应的Field Name呢?

Java8中给我们提供了实现方式,首先要做的就是定义一个可序列化的函数式接口(实现Serializable),实现如下:

/*** Created by bruce on 2020/4/10 14:16*/
@FunctionalInterface
public interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}

而在使用时,我们需要传递Getter方法引用

 //方法引用
SerializableFunction<People, String> getName1 = People::getName;
Field field = ReflectionUtil.getField(getName1);

下面看具体怎么解析这个SerializableFunction,完整实现如下ReflectionUtil

public class ReflectionUtil {private static Map<SerializableFunction<?, ?>, Field> cache = new ConcurrentHashMap<>();public static <T, R> String getFieldName(SerializableFunction<T, R> function) {Field field = ReflectionUtil.getField(function);return field.getName();}public static Field getField(SerializableFunction<?, ?> function) {return cache.computeIfAbsent(function, ReflectionUtil::findField);}public static Field findField(SerializableFunction<?, ?> function) {Field field = null;String fieldName = null;try {// 第1步 获取SerializedLambdaMethod method = function.getClass().getDeclaredMethod("writeReplace");method.setAccessible(Boolean.TRUE);SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);// 第2步 implMethodName 即为Field对应的Getter方法名String implMethodName = serializedLambda.getImplMethodName();if (implMethodName.startsWith("get") && implMethodName.length() > 3) {fieldName = Introspector.decapitalize(implMethodName.substring(3));} else if (implMethodName.startsWith("is") && implMethodName.length() > 2) {fieldName = Introspector.decapitalize(implMethodName.substring(2));}// 第3步 获取的Class是字符串,并且包名是“/”分割,需要替换成“.”,才能获取到对应的Class对象String declaredClass = serializedLambda.getImplClass().replace("/", ".");Class<?> aClass = Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());// 第4步  Spring 中的反射工具类获取Class中定义的Fieldfield = ReflectionUtils.findField(aClass, fieldName);} catch (Exception e) {e.printStackTrace();}// 第5步 如果没有找到对应的字段应该抛出异常if (field != null) {return field;}throw new NoSuchFieldError(fieldName);}
}

该类中主要有如下三个方法

  • String getFieldName(SerializableFunction function) 获取Field的字符串name
  • Field getField(SerializableFunction function)从缓存中查询方法引用对应的Field,如果没有则通过findField(SerializableFunction function)方法反射获取
  • Field findField(SerializableFunction function) 反射获取方法应用对应的Field

实现原理

1、首先我们看最后一个方法Field findField(SerializableFunction function),该方法中第一步是通过SerializableFunction对象获取Class,即传递的方法引用,然后反射获取writeReplace()方法,并调用该方法获取导SerializedLambda对象。
2、SerializedLambda是Java8中提供,主要就是用于封装方法引用所对应的信息,主要的就是方法名、定义方法的类名、创建方法引用所在类。
3、拿到这些信息后,便可以通过反射获取对应的Field。
4、而在方法Field getField(SerializableFunction function)中对获取到的Field进行缓存,避免每次都反射获取,造成资源浪费。

除此之外似乎还有一些值得思考的问题

writeReplace()方法是哪来的呢?

首先简单了解一下java.io.Serializable接口,该接口很常见,我们在持久化一个对象或者在RPC框架之间通信使用JDK序列化时都会让传输的实体类实现该接口,该接口是一个标记接口没有定义任何方法,但是该接口文档中有这么一段描述:

Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private, protected and package-private access. Subclass access to this method follows java accessibility rules.

概要意思就是说,如果想在序列化时改变序列化的对象,可以通过在实体类中定义任意访问权限的Object writeReplace()来改变默认序列化的对象。

那么我们的定义的SerializableFunction中并没有定义writeReplace()方法,这个方法是哪来的呢?
代码中SerializableFunction,Function只是一个接口,但是其在最后必定也是一个实现类的实例对象,而方法引用其实是在运行时动态创建的,当代码执行到方法引用时,如People::getName,最后会经过
java.lang.invoke.LambdaMetafactory
java.lang.invoke.InnerClassLambdaMetafactory
去动态的创建实现类。而在动态创建实现类时则会判断函数式接口是否实现了Serializable,如果实现了,则添加writeReplace()
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

值得注意的是,代码中多次编写的同一个方法引用,他们创建的是不同Function实现类,即他们的Function实例对象也并不是同一个
我们可以通过如下属性配置将 动态生成的Class保存到 磁盘上

System.setProperty("jdk.internal.lambda.dumpProxyClasses", ".");

示例代码如下:

在这里插入图片描述
动态生成的Class如下:
在这里插入图片描述

一个方法引用创建一个实现类,他们是不同的对象,那么ReflectionUtil中将SerializableFunction最为缓存key还有意义吗?

答案是**肯定有意义的!!!**因为同一方法中的定义的Function只会动态的创建一次实现类并只实例化一次,当该方法被多次调用时即可走缓存中查询该方法引用对应的Field
在这里插入图片描述
这里的缓存Key应该选用SerializableFunction#Class还是SerializableFunction实例对象好呢?

看到有些实现使用SerializableFunction的Class作为缓存key,代码如下:

public static Field getField(SerializableFunction<?, ?> function) {//使用SerializableFunction的Class作为缓存key,导致每次都调用function.getClass()return cache.computeIfAbsent(function.getClass(), ReflectionUtil::findField);
}

但是个人建议采用SerializableFunction对象,因为无论方法被调用多少次,方法代码块内的方法引用对象始终是同一个,如果采用其Class做为缓存key,每次查询缓存时都需要调用native方法function.getClass()获取其Class,也是一种资源损耗。

**总结:**Java如何通过方法引用获取属性名实现及思考至此结束。直接使用ReflectionUtil即可

相关文章:

详解Java8中如何通过方法引用获取属性名/::的使用

在我们开发过程中常常有一个需求&#xff0c;就是要知道实体类中Getter方法对应的属性名称&#xff08;Field Name&#xff09;&#xff0c;例如实体类属性到数据库字段的映射&#xff0c;我们常常是硬编码指定 属性名&#xff0c;这种硬编码有两个缺点。 1、编码效率低&#x…...

0106广度优先搜索和最短路径-无向图-数据结构和算法(Java)

1 单点最短路径 单点最短路径。 给定一幅图和一个起点s&#xff0c;回答“从s到给定目的顶点v是否存在一条路径&#xff1f;如果有&#xff0c;找出其中最短的那条&#xff08;所含边数最少&#xff09;。“等类似问题。 深度优先搜索在这个问题上没有什么作为&#xff0c;因为…...

僵尸(Zombie)进程

文章目录1.僵尸进程2.产生僵尸进程的原因3.利用 wait 函数销毁僵尸进程4.使用 waitpid 函数销毁僵尸进程1.僵尸进程 进程完成工作后&#xff08;执行完 main 函数中的程序后&#xff09;应被销毁&#xff0c;但有时这些进程将变成僵尸进程&#xff0c;占用系统中的重要资源。这…...

JS实现:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

题目&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; 数列是 1,1,2,3,5,8,13,21....观察可以看出来从第三个数字开始…...

Verilog如何编写一个基础的Testbench

本文将讲述如何使用Verilog 编写一个基础的测试脚本&#xff08;testbench&#xff09;。在考虑一些关键概念之前&#xff0c;先来看看testbench的架构是什么样的。架构包括建模时间、initial块&#xff08;initial block&#xff09;和任务&#xff08;task&#xff09;。此文…...

基于JavaEE社区物业管理系统开发与实现(附源码资料)

文章目录1. 适用人群2. 你将收获3.项目简介4.技术栈5.测试账号6.部分功能模块展示6.1.管理员6.2.业主1. 适用人群 本课程主要是针对计算机专业相关正在做毕业设计或者是需要实战项目的Java开发学习者。 2. 你将收获 提供&#xff1a;项目源码、项目文档、数据库脚本、软件工…...

问一下ChatGPT:DIKW金字塔模型

经常看到这张DIKW金字塔模型图&#xff0c;还看到感觉有点过份解读的图&#xff0c;后面又加上了insight&#xff0c;impact等内容。 Data&#xff1a;是数据&#xff0c;零散的、无规则的呈现到人们眼前&#xff0c;如果你只看到这些数字&#xff0c;如果没有强大的知识背景&a…...

javaScript基础面试题 ---闭包

闭包1、闭包是什么&#xff1f;2、闭包可以解决什么问题&#xff1f;3、闭包的缺点1、闭包是什么&#xff1f; 闭包是一个函数加上到创建这个函数的作用域的链接&#xff0c;就是一个作用域可以访问到另一个作用域的变量&#xff0c;闭包‘关闭’了函数的自由变量 function f…...

如何自定义您的网站实时聊天图标

实时聊天图标是您网站上的一个按钮&#xff0c;可在访问者单击时打开实时聊天。它代表了您的企业与客户沟通的门户。这是您的网站访问者与您联系、提出问题和接收个性化推荐的一种方式&#xff0c;聊天图标的设计最好是简单且引人入胜&#xff0c;个性化的图标往往更能提现企业…...

Vue侦听器Watch

31. Vue侦听器Watch 1. 定义 Watch是Vue.js提供的一个观察者模式&#xff0c;用于监听数据的变化并执行相应的回调函数。虽然计算属性Computed在大多数情况下更合适&#xff0c;但有时也需要一个自定义的侦听器Watch。因为在有些情况下&#xff0c;我们需要在状态变化时执行一…...

云快充研发中心平台架构师谈云原生稳定性建设之路

作者&#xff1a;吕周洋 大家好&#xff0c;我是来自云快充研发中心的平台架构师吕周洋&#xff0c;今天我给大家分享云快充云原生稳定性之路。 点击查看&#xff1a;云快充研发中心平台架构师 吕周洋&#xff1a;云快充云原生稳定性治理之路 云快充成立于2016年&#xff0c…...

ENVI IDL学习笔记之基本操作

前言ENVI IDL&#xff08;交互式数据语言&#xff09;是一个通用的科学计算包&#xff0c;它提供了一套数学函数、数据分析工具&#xff0c;以及一些科学可视化和动画工具。IDL 是 ENVI 图像处理和分析软件的基础&#xff0c;可用于编写脚本并自动执行许多使用 ENVI 图形用户界…...

多线程面试题

1. Sychronized的锁升级过程是怎样的&#xff1f; 2. Tomcat 中为什么要使用自定义类加载器&#xff1f; 3. 说说对线程安全的理解 4. 对守护线程的理解 5. 并发、并行、串行之间的区别 6. Java死锁如何避免&#xff1f; 7. 谈谈你对AQS的理解&#xff0c;AQS如何实现可重入锁&…...

YARN运行流程

YARN是Hadoop资源管理器&#xff0c;他是一个通用资源管理平台和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度&#xff0c;MapReduce等运算程序则相当于运行于操作系统上的应用程序&#xff0c;YARN为这些程序提供运算所需的资源内存、cpu。 YARN并不清楚用户提…...

java八股系列——SpringMVC从接受请求到完成响应的过程

Spring的MVC框架是围绕一个DispatcherServlet来设计的&#xff0c;这个Servlet会把请求分发给各个处理器&#xff0c;并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等&#xff0c;甚至还能支持文件上传。 流程大致如下&#xff1a; 用户发起请求&#xff1a;用…...

Elasticsearch索引全生命周期

索引(Index)是Elasticsearch中最重要的概念之一&#xff0c;也是整个Elasticsearch操作的基础&#xff0c;它是相互关联的文档的一个集合。在Elasticsearch种&#xff0c;数据存储为 JSON 文档&#xff0c;每个文档将一组键&#xff08;字段或属性的名称&#xff09;与其对应的…...

汇编指令学习(LOOP)

一、xor异或操作&#xff0c;相同为0&#xff0c;不同为1xor eax,eaxeax异或eax&#xff0c;相同为0&#xff0c;并把结果存放到eax&#xff0c;简单说该语句就是想eax寄存器清零。二、ECX&#xff0c;计数器mov ecx,0x3将ecx寄存器设置为3三、DEC减一操作dec ecxecx寄存器的值…...

Linux 配置本地yum源

挂载光盘 进入包 配置路径&#xff0c;查看在线yum源 移动在线yum源到/home/目录下 进入vi,任意取名以.repo结尾即可 按住i进行编辑&#xff0c;输入以下内容 注意gpgcheck1是检验&#xff0c;配置本地yum源不需要检验 写入上图内容按住&#xff1a;输入wq&#xff0c;点击回车…...

【PyTorch】教程:torch.nn.LeakyReLU

torch.nn.LeakyReLU 原型 CLASS torch.nn.LeakyReLU(negative_slope0.01, inplaceFalse) 参数 negative_slope (float) – 控制负值斜率&#xff0c;默认为 1e-2inplace (bool) – in-place 操作&#xff0c;默认为 False 定义 LeakyReLU(x)max⁡(0,x)negative_slope∗min⁡…...

【刷题】-- 基础 -- 二分查找

精于结构、敏于心智、熟于代码 方式&#xff1a;对于会的代码&#xff1a;学会以最快的速度构建&#xff0c;并以最快的速度书写&#xff1b;对于不会的代码&#xff1a;学会&#xff08;以最短的路径下&#xff09;看懂别人的代码。学会使用参考文档、熟悉每一个容器。 刷题位…...

告别AI瞎编代码:手把手教你用Context7 MCP给Claude/Cursor装上“实时文档库”

告别AI幻觉代码&#xff1a;Context7 MCP与主流开发工具深度集成实战指南 每次看到AI助手生成那些无法运行的过时代码时&#xff0c;你是否也感到沮丧&#xff1f;作为深度依赖AI编程助手的开发者&#xff0c;我们都经历过这样的困境&#xff1a;花费数小时调试一段本不该出现的…...

Phi-3-Mini-128K应用场景:新能源电池BMS固件日志智能归因与故障预测

Phi-3-Mini-128K应用场景&#xff1a;新能源电池BMS固件日志智能归因与故障预测 想象一下&#xff0c;你是一家新能源车企的BMS&#xff08;电池管理系统&#xff09;软件工程师。凌晨三点&#xff0c;你的手机响了&#xff0c;生产线告警&#xff1a;一批电池包的固件在测试中…...

QDKTAI实战面试题50问之41-50

一、逐题详细解析&#xff08;41-50题&#xff09;第41题&#xff1a;如何设计一个有效的AI内容审核系统&#xff1f;1. 考察重点对大模型“生成式而非判别式”核心特性的理解识别AI审核场景中的关键冲突点&#xff08;长文本处理、语气/风格干扰&#xff09;针对性解决方案的设…...

从CTF逆向实战出发:手把手教你用Python脚本破解RC4和Base58加密(附完整代码)

从CTF逆向实战出发&#xff1a;手把手教你用Python脚本破解RC4和Base58加密&#xff08;附完整代码&#xff09; 在CTF竞赛中&#xff0c;逆向工程题目往往涉及各种加密算法的识别与破解。本文将聚焦两种常见加密方式——RC4和Base58&#xff0c;通过Python脚本实现从算法识别到…...

Cursor Pro功能扩展工具:技术原理与开源解决方案

Cursor Pro功能扩展工具&#xff1a;技术原理与开源解决方案 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your trial re…...

大多数开发者还以为2026年AI编码拼的是模型,其实竞争早已转向系统架构

最近刷到Qoder和几个大厂的分享&#xff0c;我瞬间意识到&#xff1a;AI编码的战场已经彻底变天了。 很多人还在卷模型参数、卷上下文长度&#xff0c;以为下一个SOTA模型出来就能让Agent“起飞”。但真实情况是——Stripe每周合并1300个完全由Agent写的PR&#xff0c;Ramp有30…...

工业协议通信开发实战:lib60870开源库完全指南

工业协议通信开发实战&#xff1a;lib60870开源库完全指南 【免费下载链接】lib60870 Official repository for lib60870 an implementation of the IEC 60870-5-101/104 protocol 项目地址: https://gitcode.com/gh_mirrors/li/lib60870 在工业自动化领域&#xff0c;设…...

Llama-3.2V-11B-cot多轮对话效果展示:复杂技术问题拆解与解答

Llama-3.2V-11B-cot多轮对话效果展示&#xff1a;复杂技术问题拆解与解答 最近在测试各种大模型时&#xff0c;我特意找了一个比较“刁钻”的场景&#xff1a;让模型来解答一个复杂的系统设计问题。这类问题通常不是一两句话能说清的&#xff0c;它需要模型有很强的逻辑推理能…...

FineBI连接MySQL实战:手把手教你从零搭建第一个学生数据分析看板

FineBI连接MySQL实战&#xff1a;从零构建学生数据分析看板 当教务系统的学生信息沉睡在MySQL数据库中时&#xff0c;FineBI能像魔法师一样将它们唤醒为生动的可视化图表。我曾为某高校搭建第一个招生分析看板时&#xff0c;仅用三小时就让校领导看到了历年录取数据的立体画像—…...

3个核心突破:LangChain的大语言模型应用开发指南

3个核心突破&#xff1a;LangChain的大语言模型应用开发指南 【免费下载链接】langchain LangChain是一个由大型语言模型 (LLM) 驱动的应用程序开发框架。。源项目地址&#xff1a;https://github.com/langchain-ai/langchain 项目地址: https://gitcode.com/GitHub_Trending…...