Java值传递#
一句话答案#
Java 只有值传递。基本类型传的是值的副本,引用类型传的是引用地址的副本——方法内能改对象内容(影响外部),但重新赋值引用不影响外部实参。
核心要点
核心结论:Java 中一切都是值传递(pass-by-value)。 区别只在”传的值”是什么。
1. 基本类型:传值的副本
void change(int x) { x = 100; }
int a = 1; change(a); // a 仍是 1java方法内的 x 是 a 的拷贝,互不影响。
2. 引用类型:传引用地址的副本
引用本身(地址)被拷贝,但副本和原引用指向同一个对象。
void modify(StringBuilder sb) { sb.append("B"); } // 改的是同一对象 → 外部可见
void reassign(StringBuilder sb) { sb = new StringBuilder("X"); } // 只改副本指向 → 外部不变
StringBuilder s = new StringBuilder("A");
modify(s); // s = "AB" (操作了同一对象)
reassign(s); // s = "AB" (副本被指向新对象,原引用没动)java3. 经典的”交换”反例
void swap(Object a, Object b) { Object t = a; a = b; b = t; } // 无效!只交换了副本引用java方法内交换的是两个引用副本,原始引用纹丝不动。
4. 为什么说没有引用传递(pass-by-reference)?
引用传递意味着传的是变量本身(实参的地址),方法内重新赋值会反映到外部。Java 做不到——reassign 内重新 new 后外部引用不变,证明传进去的只是引用的拷贝。C++ 的 & 引用才是真正的引用传递。
面试回答(2分钟版)
Java 中只有值传递,没有引用传递,这是一个非常容易混淆的点。所谓值传递,就是方法接收的是实参的一个副本。对基本类型来说很直观,传进去的是值的拷贝,方法内怎么改都不影响外面的变量。对引用类型,传进去的是引用地址的拷贝——注意是地址的拷贝,不是对象的拷贝。所以方法内的引用副本和外部引用指向同一个对象,如果我通过这个引用去修改对象的内容,比如给 List add 元素、改对象的字段,外部是能看到变化的,因为操作的是同一个对象。但如果我在方法内把这个引用重新赋值指向一个新对象,那只是改了副本的指向,外部的原引用还指向老对象,完全不受影响。判断 Java 是不是引用传递,最经典的就是写一个 swap 交换方法,会发现交换无效,因为交换的只是两个引用副本。真正的引用传递像 C++ 的 & 引用,方法内重新赋值会反映到外部,Java 做不到。所以准确表述是:Java 永远是值传递,只不过引用类型传递的”值”是引用地址的副本。
追问与易错
追问方向:
- “方法内给传入的 List add 元素,外部能看到吗?”→ 能。引用副本和外部引用指向同一对象,修改对象内容外部可见
- “方法内把传入的 List 重新 new 一个,外部变吗?”→ 不变。只改了引用副本的指向,原引用没动
- “那 Java 到底有没有引用传递?”→ 没有,只有值传递。引用类型传的是引用地址的副本,不是变量本身
- “String 作参数为什么改不了?”→ String 不可变,方法内任何”修改”都是生成新对象并让副本引用指向它,原引用不变
- “怎么实现真正的交换/输出参数?”→ 用数组/包装对象/返回值,或把要改的内容封装进对象通过其字段传递
易错点:
- ❌ “引用类型是引用传递”——传的是引用地址的副本,本质仍是值传递
- ❌ “方法内 new 一个新对象外部能拿到”——重新赋值只影响副本指向
- ✅ 一句话区分:改对象内容外部可见,改引用指向外部不可见