Java 循环中对象复用导致属性覆盖?从 JVM 内存模型讲解原因
前言:前几天下午写代码的时候遇到一个bug,是一个比较基础的问题,关于对象引用,如果只是解决问题,那么就没有写这篇文章的必要,主要是站在jvm的角度上讲一讲这个问题
//国家,币种 一个国家可以对应多个币种 bankCountries是以及处理好的结果集
//CountryCurrency:String country; List<String> currencies
//CountryCurrTmp:String country; String currency; String payNo;
List<CountryCurrTmp> countryCurr = new ArrayList<>();
for (ClientCountry bankCountry : bankCountries) {
CountryCurrTmp countryCurrTmp = new CountryCurrTmp();
// 业务1~ 国家转编码
NationalityInfoEnum enumByShortCode = NationalityInfoEnum.getEnumByShortCode(bankCountry.getCountry());
if(enumByShortCode!=null){
countryCurrTmp.setCountry(enumByShortCode.getCode());
}
// 业务2~ 币种转编码
for (String currency : bankCountry.getCurrencies()) {
CurrencyEnum currencyEnum = CurrencyEnum.getByCurrCode(currency);
if (currencyEnum != null) {
countryCurrTmp.setCurrency(currencyEnum.getNumCode());
}
countryCurrTmp.setPayNo(payNo);
countryCurr.add(countryCurrTmp);
}
}
return countryCurr;
结果:写这段代码 我期待的结果是得到一个CountryCurrTmp集合 里面是处理好的 国家 币种 以及对应的支付方式,但实际上执行上述代码会发现 每一个country对应的币种最终都会一样且是内层循环最后遍历的币种.
分析:站在jvm的角度上解释一下这个问题,外层for循环中countryCurrTmp对象创建,是在堆内存中开辟一片空间来存放的,而当线程执行到这段代码的时候,会在虚拟机栈中创建一个栈帧(包含局部变量表,操作数栈),而countryCurrTmp作为局部变量,存放在局部变量表中的是引用(堆中的地址)而不是副本,同一个国家 每次内循环赋值currency的时候都修改的是同一个堆内存中的对象,即造成了对象的复用,所以才会造成结果集每一个country对应的币种都是最后赋值的那个,从而使数据出现问题.下面会讲一个详细的实例
示例:
- 1.外层循环创建CountryCurrTmp时,JVM 会在堆内存中开辟一块空间(比如地址0x123),存储该对象的country“美国”、初始currency(空)等属性;
- 2.线程执行这段代码时,会在虚拟机栈中创建一个 “方法栈帧”,其中的 “局部变量表” 会存储countryCurrTmp这个变量的引用,而是堆内存的地址0x123;
- 3.进入内层循环遍历币种:第一次改currency为 “USD”,本质是通过0x123找到堆中的对象,修改其currency字段;第二次改currency为“EUR”,还是通过同一个0x123修改同一个堆对象 —— 最终list中添加的 2 个元素,都是指向0x123的引用,自然会显示同一个“EUR”。
总结一下,当初写代码的时候我没有意识到这个问题,就说明我对这个知识点不熟练(JVM对象引用),有了一些业务的干扰就写出了错误的代码,所以写篇文章分享一下吧. 最后改后的代码:
List<CountryCurrTmp> tmpCountryCurr = new ArrayList<>();
for (ClientCountry bankCountry : bankCountries) {
// 1. 转换国家代码
NationalityInfoEnum countryEnum = NationalityInfoEnum.getEnumByShortCode(bankCountry.getCountry());
if (countryEnum == null) {
log.warn("未找到对应的国家枚举,跳过处理: 国家代码={}", bankCountry.getCountry());
continue;
}
// 2. 币种存储数字编码
for (String currency : bankCountry.getCurrencies()) {
CurrencyEnum currencyEnum = CurrencyEnum.getByCurrCode(currency);
if (currencyEnum == null) {
log.warn("未找到币种的数字编码,跳过处理: 币种代码={}", currency);
continue;
}
CountryCurrTmp countryCurrTmp = new CountryCurrTmp();
countryCurrTmp.setCountry(countryEnum.getCode());
countryCurrTmp.setPayNo(payNo);
countryCurrTmp.setCurrency(currencyEnum.getNumCode());
tmpCountryCurr.add(countryCurrTmp);
}
}
return tmpCountryCurr;
Java 循环中对象复用导致属性覆盖?从 JVM 内存模型讲解原因的更多相关文章
- Java面试- JVM 内存模型讲解
经常有人会有这么一个疑惑,难道 Java 开发就一定要懂得 JVM 的原理吗?我不懂 JVM ,但我照样可以开发.确实,但如果懂得了 JVM ,可以让你在技术的这条路上走的更远一些. JVM 的重要性 ...
- java中栈内存与堆内存(JVM内存模型)
java中栈内存与堆内存(JVM内存模型) Java中堆内存和栈内存详解1 和 Java中堆内存和栈内存详解2 都粗略讲解了栈内存和堆内存的区别,以及代码中哪些变量存储在堆中.哪些存储在栈中.内存中的 ...
- SQLServer数据库中开启CDC导致事务日志空间被占满的原因
SQLServer数据库中开启CDC导致事务日志空间被占满的原因 转载 2017-04-01 投稿:mrr 我要评论 这篇文章主要介绍了SQLServer数据库中开启CDC导致事务日志空间 ...
- Java内存管理-JVM内存模型以及JDK7和JDK8内存模型对比总结(三)
勿在流沙住高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分享了JVM及其启动流程,今天介绍一下JVM内部的一些区域,以及具体的区域在运行 ...
- java 深入理解jvm内存模型 jvm学习笔记
jvm内存模型 这是java堆和方法区内存模型 参考:https://www.cnblogs.com/honey01/p/9475726.html Java 中的堆也是 GC 收集垃圾的主要区域.GC ...
- JVM内存模型总结,有各版本JDK对比、有元空间OOM监控案例、有Java版虚拟机,综合实践学习!
作者:小傅哥 博客:https://bugstack.cn Github:https://github.com/fuzhengwei/CodeGuide/wiki 沉淀.分享.成长,让自己和他人都能有 ...
- Java基础知识强化100:JVM 内存模型
一. JVM内存模型总体架构图: 方法区和堆由所有线程共享,其他区域都是线程私有的 二. JVM内存模型的结构分析: 1. 类装载器(classLoader) 类装载器,它是在java虚拟机中用途是 ...
- 深入理解java虚拟机学习笔记(一)JVM内存模型
上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了.这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状态和激情都要 ...
- 解决 java循环中使用 Map时 在put值时value值被覆盖的问题
其实很简单,只需要把容器换成list 然后在循环中,每次循环末尾map = new HashMap() 或者直接在循环中一开始就实例化hashmap(Map map = new HashMap();) ...
- Java循环中删除一个列表元素
本文主要想讲述一下我对之前看到一篇文章的说法.假设跟你的想法有出入,欢迎留言.一起讨论. #3. 在循环中删除一个列表元素 考虑以下的代码.迭代过程中删除元素: ArrayList<String ...
随机推荐
- 高效存储的秘诀:bitmap 数据结构在标签中的应用
在当今大数据和信息爆炸的时代,如何有效地管理和查询海量的数据成为了企业和开发者面临的重大挑战.其中,标签系统作为数据管理中的一种重要手段,被广泛应用于用户画像.商品分类.内容推荐等多个场景.然而,随着 ...
- Go + WebSocket + Chrome Extension:基于真实浏览器环境的 cf_clearance 自动化获取方案
前言 随着 Web 安全防护技术的演进,Cloudflare 等 CDN 服务商部署的反爬虫机制愈发复杂.传统的 HTTP 客户端库已无法有效应对基于 JavaScript 执行的挑战验证,而 Sel ...
- Vertx 实现webapi实战项目(四)
本节主要介绍使用消息解析和handler分发 一:定义一个常量类,储存消息id public class HandlerCode { /***** 测试接口 ******/ public static ...
- 八、make编译输出重定向
4.编译输出重定向 将 make 命令的标准输出(stdout)和标准错误输出(stderr)重定向到文件,以便于查看编译日志,快速分析定位问题. 1.重定向到同一个文件 语法: make > ...
- Bug报告 5C标准说明与示例
以下是基于5C标准的缺陷报告编写指南及具体示例,帮助团队高效提交高质量Bug报告: Bug报告 5C标准说明与示例 1. Correct(准确) 要求:描述无歧义,技术细节精确,避免主观推测. 反例: ...
- vant批量选择删除列表
代码如下 <template> <div class="wrapp"> <button @click="onEdit">{{ ...
- qt 鼠标右键菜单
解决方案: 重写 MainWindow::contextMenuEvent(QContextMenuEvent *event)函数即可 void MainWindow::contextMenuEven ...
- SciTech-EECS-Manufacturing-PCB Layout基础知识
SciTech-EECS-Manufacturing-PCB Layout基础知识 PCB Layout基础知识 PCB Layout是硬件电子设备必不可少的一个环节. PCB Layout的规范及质 ...
- SciTech-BigDataAIML-Tensorflow-模型的训练与评估: tf.keras.losses + tf.keras.optimizer + tf.keras.metrics
模型的训练: tf.keras.losses 和 tf.keras.optimizer 定义一些模型超参数: num_epochs = 5 batch_size = 50 learning_rate ...
- Windows10系统电脑麦克风没声音的问题
有电脑基地的用户在使用win10系统的时候,出现使用耳机连接的麦克风不能用,和别人聊天的时候没有声音.小伙伴很着急,不知道该怎么办?下面技术员小编就来分享详细的解决方法. Windows 10 下麦克 ...