Java 循环中对象复用导致属性覆盖?从 JVM 内存模型讲解原因

Java 对象复用与 JVM 内存模型解析

前言:前几天下午写代码的时候遇到一个bug,是一个比较基础的问题,关于对象引用,如果只是解决问题,那么就没有写这篇文章的必要,主要是站在jvm的角度上讲一讲这个问题

/* by yours.tools - online tools website : yours.tools/zh/shortcut.html */
//国家,币种  一个国家可以对应多个币种  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对象引用),有了一些业务的干扰就写出了错误的代码,所以写篇文章分享一下吧. 最后改后的代码:

/* by yours.tools - online tools website : yours.tools/zh/shortcut.html */
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;
(SCI三维路径规划对比)25年最新五种智能算法优化解决无人机路径巡检三维路径规划对比(灰雁算法真菌算法吕佩尔狐阳光生长研究(Matlab代码实现)内容概要:本文档主要介绍了一项关于无人机三维路径巡检规划的研究,通过对比2025年最新的五种智能优化算法(包括灰雁算法、真菌算法、吕佩尔狐算法、阳光生长算法等),在复杂三维环境中优化无人机巡检路径的技术方案。所有算法均通过Matlab代码实现,并重点围绕路径安全性、效率、能耗和避障能力进行性能对比分析,旨在为无人机在实际巡检任务中的路径规划提供科学依据和技术支持。文档还展示了多个相关科研方向的案例与代码资源,涵盖路径规划、智能优化、无人机控制等多个领域。; 适合人群:具备一定Matlab编程基础,从事无人机路径规划、智能优化算法研究或自动化、控制工程方向的研究生、科研人员及工程技术人员。; 使用场景及目标:① 对比分析新型智能算法在三维复杂环境下无人机路径规划的表现差异;② 为科研项目提供可复现的算法代码与实验基准;③ 支持无人机巡检、灾害监测、电力线路巡查等实际应用场景的路径优化需求; 阅读建议:建议结合文档提供的Matlab代码进行仿真实验,重点关注不同算法在收敛速度、路径长度和避障性能方面的表现差异,同时参考文中列举的其他研究案例拓展思路,提升科研创新能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值