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

JVM如何实现原始类型包装类作函数参数时的不可变性

Integer自增与参数传递的底层机制

首先明确两个核心前提:

  • Java 所有参数传递均为值传递:对于引用类型,传递的是变量存储的对象引用的副本,而非引用本身,更不是堆上的对象实体。
  • Integer 是不可变类:其内部存储实际数值的字段为 private final int value,类本身没有提供任何修改该字段的公开方法,正常代码路径下无法修改一个已创建Integer实例的内部值。

编译器对Integer类型++i的语法糖处理

不管++i写在哪个方法里,只要操作的是Integer类型变量,编译器都会自动插入拆箱、计算、装箱的逻辑,等价代码如下:

// 源码写的 ++i; 编译后实际执行逻辑
i = Integer.valueOf(i.intValue() + 1);

这个操作从来没有修改原Integer对象的内部值,而是生成了一个新的(或从Integer缓存池取出对应值的)Integer实例,再把当前作用域下的变量i重新指向这个新实例。


两个场景的行为差异解释

main方法内的++i生效原因

main方法栈帧中的局部变量i初始指向你创建的、存储值为3的Integer实例:

  1. 执行++i时先拆箱取出值3,计算得到4
  2. 通过Integer.valueOf(4)获取存储值为4的Integer实例
  3. 将main栈帧内的局部变量i重新指向这个值为4的实例

原存储3的Integer实例没有任何修改,只是变量不再指向它,因此后续打印得到4。

方法内++i不影响外部变量的原因

调用changeInteger(i)时,JVM会将main方法中i存储的引用(指向值为4的Integer实例的地址)拷贝一份,传入changeInteger方法的栈帧,赋值给形参i。此时两个作用域的i是完全独立的变量,只是初始指向堆上同一个Integer实例。

在changeInteger内执行++i时:

  1. 同样拆箱计算得到5,获取值为5的Integer实例
  2. 仅将changeInteger栈帧内的形参i重新指向这个值为5的新实例

整个过程不会触碰main方法栈帧内的局部变量i,方法执行结束出栈后,形参i直接被回收,main中的i始终指向原来值为4的实例,因此打印结果仍为4。


常见误区澄清

不少人会混淆“修改可变对象的内部状态”和“给变量重新赋值”的区别:如果传入方法的是可变对象(比如ArrayList、自定义的有setter的普通对象),方法内通过引用调用对象的修改方法(如add()、setXxx()),是直接修改堆上的对象本身,外部变量指向同一个对象,自然能看到变化。但对于不可变类,所有看似“修改”的操作本质都是生成新对象+当前作用域变量重定向,完全无法跨作用域影响其他变量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:24:35