详解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的字符串nameField 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中如何通过方法引用获取属性名/::的使用
在我们开发过程中常常有一个需求,就是要知道实体类中Getter方法对应的属性名称(Field Name),例如实体类属性到数据库字段的映射,我们常常是硬编码指定 属性名,这种硬编码有两个缺点。 1、编码效率低&#x…...
0106广度优先搜索和最短路径-无向图-数据结构和算法(Java)
1 单点最短路径 单点最短路径。 给定一幅图和一个起点s,回答“从s到给定目的顶点v是否存在一条路径?如果有,找出其中最短的那条(所含边数最少)。“等类似问题。 深度优先搜索在这个问题上没有什么作为,因为…...
僵尸(Zombie)进程
文章目录1.僵尸进程2.产生僵尸进程的原因3.利用 wait 函数销毁僵尸进程4.使用 waitpid 函数销毁僵尸进程1.僵尸进程 进程完成工作后(执行完 main 函数中的程序后)应被销毁,但有时这些进程将变成僵尸进程,占用系统中的重要资源。这…...
JS实现:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
题目:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 数列是 1,1,2,3,5,8,13,21....观察可以看出来从第三个数字开始…...
Verilog如何编写一个基础的Testbench
本文将讲述如何使用Verilog 编写一个基础的测试脚本(testbench)。在考虑一些关键概念之前,先来看看testbench的架构是什么样的。架构包括建模时间、initial块(initial block)和任务(task)。此文…...
基于JavaEE社区物业管理系统开发与实现(附源码资料)
文章目录1. 适用人群2. 你将收获3.项目简介4.技术栈5.测试账号6.部分功能模块展示6.1.管理员6.2.业主1. 适用人群 本课程主要是针对计算机专业相关正在做毕业设计或者是需要实战项目的Java开发学习者。 2. 你将收获 提供:项目源码、项目文档、数据库脚本、软件工…...
问一下ChatGPT:DIKW金字塔模型
经常看到这张DIKW金字塔模型图,还看到感觉有点过份解读的图,后面又加上了insight,impact等内容。 Data:是数据,零散的、无规则的呈现到人们眼前,如果你只看到这些数字,如果没有强大的知识背景&a…...
javaScript基础面试题 ---闭包
闭包1、闭包是什么?2、闭包可以解决什么问题?3、闭包的缺点1、闭包是什么? 闭包是一个函数加上到创建这个函数的作用域的链接,就是一个作用域可以访问到另一个作用域的变量,闭包‘关闭’了函数的自由变量 function f…...
如何自定义您的网站实时聊天图标
实时聊天图标是您网站上的一个按钮,可在访问者单击时打开实时聊天。它代表了您的企业与客户沟通的门户。这是您的网站访问者与您联系、提出问题和接收个性化推荐的一种方式,聊天图标的设计最好是简单且引人入胜,个性化的图标往往更能提现企业…...
Vue侦听器Watch
31. Vue侦听器Watch 1. 定义 Watch是Vue.js提供的一个观察者模式,用于监听数据的变化并执行相应的回调函数。虽然计算属性Computed在大多数情况下更合适,但有时也需要一个自定义的侦听器Watch。因为在有些情况下,我们需要在状态变化时执行一…...
云快充研发中心平台架构师谈云原生稳定性建设之路
作者:吕周洋 大家好,我是来自云快充研发中心的平台架构师吕周洋,今天我给大家分享云快充云原生稳定性之路。 点击查看:云快充研发中心平台架构师 吕周洋:云快充云原生稳定性治理之路 云快充成立于2016年,…...
ENVI IDL学习笔记之基本操作
前言ENVI IDL(交互式数据语言)是一个通用的科学计算包,它提供了一套数学函数、数据分析工具,以及一些科学可视化和动画工具。IDL 是 ENVI 图像处理和分析软件的基础,可用于编写脚本并自动执行许多使用 ENVI 图形用户界…...
多线程面试题
1. Sychronized的锁升级过程是怎样的? 2. Tomcat 中为什么要使用自定义类加载器? 3. 说说对线程安全的理解 4. 对守护线程的理解 5. 并发、并行、串行之间的区别 6. Java死锁如何避免? 7. 谈谈你对AQS的理解,AQS如何实现可重入锁&…...
YARN运行流程
YARN是Hadoop资源管理器,他是一个通用资源管理平台和调度平台,可为上层应用提供统一的资源管理和调度,MapReduce等运算程序则相当于运行于操作系统上的应用程序,YARN为这些程序提供运算所需的资源内存、cpu。 YARN并不清楚用户提…...
java八股系列——SpringMVC从接受请求到完成响应的过程
Spring的MVC框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。 流程大致如下: 用户发起请求:用…...
Elasticsearch索引全生命周期
索引(Index)是Elasticsearch中最重要的概念之一,也是整个Elasticsearch操作的基础,它是相互关联的文档的一个集合。在Elasticsearch种,数据存储为 JSON 文档,每个文档将一组键(字段或属性的名称)与其对应的…...
汇编指令学习(LOOP)
一、xor异或操作,相同为0,不同为1xor eax,eaxeax异或eax,相同为0,并把结果存放到eax,简单说该语句就是想eax寄存器清零。二、ECX,计数器mov ecx,0x3将ecx寄存器设置为3三、DEC减一操作dec ecxecx寄存器的值…...
Linux 配置本地yum源
挂载光盘 进入包 配置路径,查看在线yum源 移动在线yum源到/home/目录下 进入vi,任意取名以.repo结尾即可 按住i进行编辑,输入以下内容 注意gpgcheck1是检验,配置本地yum源不需要检验 写入上图内容按住:输入wq,点击回车…...
【PyTorch】教程:torch.nn.LeakyReLU
torch.nn.LeakyReLU 原型 CLASS torch.nn.LeakyReLU(negative_slope0.01, inplaceFalse) 参数 negative_slope (float) – 控制负值斜率,默认为 1e-2inplace (bool) – in-place 操作,默认为 False 定义 LeakyReLU(x)max(0,x)negative_slope∗min…...
【刷题】-- 基础 -- 二分查找
精于结构、敏于心智、熟于代码 方式:对于会的代码:学会以最快的速度构建,并以最快的速度书写;对于不会的代码:学会(以最短的路径下)看懂别人的代码。学会使用参考文档、熟悉每一个容器。 刷题位…...
Spark MLlib 特征工程
Spark MLlib 特征工程预处理特征选择归一化离散化Embedding向量计算特征工程制约了模型效果 : 决定了模型效果的上限 , 而模型调优只是在不停地逼近上限好的特征工程才能有好的模型 特征处理函数分类 : 预处理 : 将模型无法直接消费的数据,转为可消费的数据形式特…...
CentOS7 完全卸载 php
在 CentOS 7 使用 yum install 简单安装 php 后,发现 php 版本 5.4 ,太低了! 然后,使用 yum remove 简单卸载后,发现 php 还在,不干净! 只好 rpm 慢慢卸载 rpm -qa |grep php php-gd-5.4.16-4…...
关于OCS认证里必须知晓的内容
【关于OCS认证里必须知晓的内容】美国非营利组织Textile Exchange推出的有机认证标准——有机含量标准(The Organic Content Standard),简称OCS。该标准通过跟踪有机原材料的种植从而监管整个有机产业链。OCS将应用于各种有机种植原料的验证,而不只限于有…...
创业做电商难不难?新人做电商怎么才能挣钱?
这几年经济不景气,创业做电商的人越来越多,但是,对于多数人来说,一开始做电商,都是试错成本,没有系统学习或者是跟着半吊子二把刀学的,结果赔钱就算了,新人创业做电商到底难不难&…...
【项目设计】高并发内存池(七)[性能测试和提升]
🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…...
PHP:Laravel cast array json数据存数据库时unicode 编码问题和update更新不触发数据转换
目录问题描述问题解决方式一:自定义属性方式二:继承覆写方式三:trait复用方式四:定义Cast子类update不生效参考文章问题描述 Model示例 class UserModel extends Model {protected $table tb_user;protected $casts [alias …...
自动化测试总结--断言
采购对账测试业务流程中,其中一个测试步骤总是失败,原因是用例中参数写错及断言不明确 一、问题现象: 采购对账主流程中,其中一个步骤失败了,会导致这个套件一直失败 图(1)测试报告视图中&…...
传输线的物理基础(三):传输线的瞬时阻抗
每个信号都有一个上升时间 RT,通常是从 10% 到 90% 的电压电平测量的。当信号沿传输线向下移动时,前沿在传输线上展开并具有空间范围。如果我们可以冻结时间并观察电压分布向外移动时的大小,我们会发现类似下图的东西。传输线上上升时间的长度…...
第六章:多线程
第六章:多线程 6.1:程序、进程、线程基本概念 程序 程序program是为了完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程 进程process是程序的一次执行过程,或是正在运行的一个程序。是一个…...
铁路与公路
蓝桥杯集训每日一题acwing4074 某国家有 n 个城市(编号 1∼n)和 m 条双向铁路。 每条铁路连接两个不同的城市,没有两条铁路连接同一对城市。 除了铁路以外,该国家还有公路。 对于每对不同的城市 x,y,当且仅当它们之…...
购物网站建设成本/鹤壁seo
负载均衡策略概述LoadBalance扩展接口AbstractLoadBalanceMockLoadBalance-伪负载均衡RandomLoadBalance-随机RoundRobinLoadBalance-轮循RoundRobinLoadBalance#doSelect(待完善)LeastActiveLoadBalance-最少活跃调用数RpcStatus#getActive-活跃数ConsistentHashLoadBalance-一…...
四方区企业型网站建设/百度一下官网首页百度
网易vip邮箱多少钱?163邮箱名字怎么起高端?邮箱名字的后缀每家邮箱都是固定的,但是前缀是可以设置的,无论是短位、寓意邮箱号都可以注册,tom邮箱每个月都会有特殊靓号的推出。 163vip邮箱登陆入口: 极致安…...
吉林省建设工程造价网站/推广方案如何写
前驱节点(predecessor) 前驱节点:中序遍历时的前一个节点。如果是二叉搜索树,前驱节点就是前一个比它小的节点。 node.left ! null 举例:6、13、8predecessor node.left.right.right.right...,终止条件&a…...
关于做公司网站建设你应该知道的/最佳磁力吧ciliba
C友元函数和友元类(C friend)详解 私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程…...
css3网站/seo外包公司兴田德润官方地址
trainorg[‘totaltt’]trainorg.apply(lambda x:x[7:17].sum(),axis1) trainorg[‘totalmax’]trainorg.apply(lambda x:x[7:17].max(),axis1) trainorg[‘totalmin’]trainorg.apply(lambda x:x[7:17].min(),axis1)...
好用的网站后台/域名申请的流程
题目: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: …...