引用类型变量存储的是对象在堆内存中的地址而非对象本身;传参时传递的是该地址的副本,故修改对象属性会影响原对象,但重赋值只改变形参地址副本。

理解“引用类型变量”到底存的是什么
引用类型变量本身不存对象数据,只存对象在堆内存中的地址(比如 0x7f8a12b4 这样的十六进制编号)。就像你家的门牌号不是房子本身,只是找到房子的线索。变量声明如 Person p = new Person(),p 是个“地址标签”,new 出的对象本体在堆里。这点是所有后续行为的起点。
值传递引用:Java 和 C# 的真实逻辑
Java 和 C# 中,把引用类型变量传给方法时,传递的不是对象,也不是“引用本身”,而是这个地址值的副本。也就是说,形参和实参各自持有一份相同的地址——指向堆中同一个对象。
- 修改对象属性(如
d.name = "Alice")会生效,因为两个变量都通过相同地址找到了那个对象 - 但让形参指向新对象(如
d = new Person()),只改了形参自己的地址副本,原变量仍指向旧对象 - 这既不是纯值传递(改属性影响了外部),也不是纯引用传递(重赋值不影响外部),而是“传递引用的值”
动手验证:用 id() 或 System.identityHashCode() 看地址
Python 可用 id(变量) 查底层地址;Java 可用 System.identityHashCode(obj)(注意不是 obj.hashCode())辅助判断是否指向同一对象。
- 调用前后打印实参和形参的 identity hash,若一致,说明它们地址相同
- 在方法内执行
obj = new Obj()后再打印,形参的 hash 会变,实参不变 - 执行
obj.field = 100后,实参对象的 field 值已更新,但地址未变
实战避坑:什么时候你以为在改对象,其实没改?
常见误操作往往源于混淆“变量”和“对象”:
- 把 String、Integer 等不可变类型当普通引用类型用:它们重赋值(
s = s + "x")实际创建新对象,原引用未变 - 在方法里新建集合再赋给形参(
list = new ArrayList()),外部 list 仍为 null 或旧引用 - 多线程环境下,仅靠地址共享不等于线程安全——需额外同步机制保护对象状态

















