方法参数的 “值传递骗局”:修改引用参数为何不改变原对象?

一、Bug 场景

在 Java 编程中,开发人员通常认为当向方法传递一个对象引用作为参数时,在方法内部对该引用所指向对象的修改会反映到方法外部的原对象上。然而,在某些情况下,他们发现即使在方法内部看似修改了对象,原对象却并未改变,这导致了业务逻辑出现错误,程序的行为不符合预期。

二、代码示例

数据类

java 复制代码
public class Data {
    private int value;

    public Data(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

方法调用类(有缺陷)

java 复制代码
public class MethodParameterBugExample {
    public static void modifyData(Data data) {
        // 创建一个新的Data对象并赋值给参数data
        data = new Data(20); 
    }

    public static void main(String[] args) {
        Data originalData = new Data(10);
        modifyData(originalData);
        System.out.println("原对象的值: " + originalData.getValue());
    }
}

三、问题描述

  1. 预期行为 :开发人员预期在调用 modifyData 方法后,originalDatavalue 值会被修改为 20。因为他们认为传递给 modifyData 方法的 data 参数是 originalData 的引用,在方法内部对 data 的修改应该会影响到 originalData
  2. 实际行为originalDatavalue 值仍然为 10。这是因为 Java 采用的是值传递,当将 originalData 作为参数传递给 modifyData 方法时,实际上传递的是 originalData 引用的副本(即一个指向 originalData 对象的指针的拷贝)。在 modifyData 方法内部,data = new Data(20); 这行代码创建了一个新的 Data 对象,并将 data 这个引用副本指向了新对象,但原对象 originalData 的引用并未改变,它仍然指向原来的对象,所以原对象的值没有被修改。

四、解决方案

  1. 正确修改对象属性:如果要修改原对象的值,应该直接修改对象的属性,而不是重新赋值引用。
java 复制代码
public class MethodParameterBugExample {
    public static void modifyData(Data data) {
        data.setValue(20); 
    }

    public static void main(String[] args) {
        Data originalData = new Data(10);
        modifyData(originalData);
        System.out.println("原对象的值: " + originalData.getValue());
    }
}
  1. 返回修改后的对象:如果确实需要创建新对象并更新原引用,可以通过方法返回新对象,并在调用处重新赋值。
java 复制代码
public class MethodParameterBugExample {
    public static Data modifyData(Data data) {
        return new Data(20); 
    }

    public static void main(String[] args) {
        Data originalData = new Data(10);
        originalData = modifyData(originalData);
        System.out.println("原对象的值: " + originalData.getValue());
    }
}
相关推荐
古法安卓2 小时前
Android-日志系统源码解析
android·java·android studio
小夏coding3 小时前
从"一把梭"到"精妙拆解" —— 滑动窗口计时框架的设计演进
java·后端
MacroZheng4 小时前
同事问我:"Claude Code经常失忆,不怕它把项目搞炸?",我:"怕,三个Markdown文件给它装个永不丢失的外置大脑!"
java·人工智能·后端
十年Java程序媛5 小时前
深度实战:JDK21 虚拟线程 + HikariCP 生产正确配比、坑点、监控全套
java·spring boot
xiaoqiMikko6 小时前
有人在搜一个不存在的 Tomcat 版本
java·tomcat
用户3126874877209 小时前
ConcurrentHashMap 怎么保证线程安全?从分段锁到 CAS+synchronized
java
vipxieliang9 小时前
ValidX vs Apache Commons Validator:功能与性能对比
java·spring boot
SimonKing9 小时前
升级Spring Boot 4后,从 Jackson 2 到 3,到底有哪些变化
java·后端·程序员
吃饱了得干活10 小时前
一篇讲清楚Spring Boot:自动装配、启动器、过滤器、拦截器、设计模式
java·spring boot·后端
lhldsg11 小时前
AI零售系统实战指南:从架构设计到落地部署全解析
java·人工智能·小程序·uni-app·零售