You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java基础问题:调用manipulate方法后Account余额为何未变为5

核心原理

Java 所有参数传递统一是值传递,不存在引用传递的机制:当传入对象类型参数时,实际传递的是「对象引用的副本」,而非原始引用本身,这就是你观察到现象的根本原因。你有C语言基础的话,这个逻辑可以直接和指针的传参逻辑对应,非常好理解。

代码执行全流程拆解

我们逐行走一遍执行逻辑,你就能完全对应上调试时看到的现象:

  • main方法执行Account c1 = new Account(100);
    堆内存中会创建一个money初始值为100的Account实例,分配的内存地址记为A;同时main方法对应的栈帧中,会创建一个名为c1的引用变量,变量里存储的值就是地址A,指向堆上的这个实例。
  • 调用manipulate(c1);
    方法调用时会为manipulate创建独立的栈帧,传入参数时会把main里c1存储的值(也就是地址A)拷贝一份,存到manipulate栈帧的参数变量c1中。注意这两个c1是完全独立的两个变量,只是此刻存储的地址值相同,都指向堆上地址A的实例。
  • manipulate内执行c1.add(120);
    方法内的c1存储的是地址A,顺着地址找到堆上的实例,调用add方法把实例的money值加120,地址A对应的实例money就变成了220。这一步是直接修改堆上的真实对象数据,所以你调试时会看到方法内c1的money确实变了。
  • manipulate内执行Account c2 = new Account(5);
    堆内存中新创建一个money=5的Account实例,分配的内存地址记为B;manipulate栈帧里的局部变量c2存储的值就是地址B,指向这个新实例。
  • manipulate内执行c1 = c2;
    这一步修改的仅仅是manipulate栈帧内参数变量c1存储的值:把原来存的地址A替换成地址B,让方法内的局部c1指向新的money=5的实例。这个修改完全不会触及main方法栈帧里的原始c1变量,main里的c1存储的始终是地址A,从来没被改动过。
  • manipulate方法执行完毕,对应的栈帧直接销毁,方法内的局部变量c1、c2随之回收。回到main方法时,main里的c1还是指向地址A对应的、money=220的实例,所以最终打印结果是220,不会变成5。
C语言逻辑对照

你可以直接把Java里的对象引用等价为C里的指针,两者传参逻辑完全一致:
Java传对象参数,相当于C函数的形参写为Account* c1,传参时把外部指针存储的地址值拷贝给形参:

  • 如果你通过形参指针操作指向的内存(比如调用c1.add(120),对应C里写c1->money += 120),修改的是内存上的真实数据,外部当然能看到变化。
  • 如果你直接给形参指针变量本身重新赋值(比如c1 = c2,对应C里写c1 = &new_account),改的只是函数内部的指针副本,根本影响不到外部的原始指针,外部指针还是指向原来的内存地址,自然感知不到这个赋值操作。

澄清一个常见误区:很多人说“Java传对象是引用传递”是完全错误的。真正的引用传递类似C++里的引用形参(比如Account* &c1),这种场景下给形参重新赋值会直接修改外部的原始变量,Java自始至终没有提供这种能力。

内容的提问来源于stack exchange,提问作者rascadux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:15:30