java 所有参数均按值传递;对对象而言,传递的是引用的副本,因此方法内通过该副本调用对象方法(如 list.add())会真实修改堆中同一对象,但重新赋值引用(如 list = new arraylist<>())仅改变副本指向,不影响原始引用。
java 所有参数均按值传递;对对象而言,传递的是引用的副本,因此方法内通过该副本调用对象方法(如 list.add())会真实修改堆中同一对象,但重新赋值引用(如 list = new arraylist<>())仅改变副本指向,不影响原始引用。
在 Java 教程与面试中,“Java 是传值还是传引用?”是一个高频误区源头。关键在于:Java 始终是传值(pass-by-value),但对象类型的“值”,本身就是指向堆内存中实例的引用(reference)。这意味着——你传递的不是对象本身,也不是 C++ 那样的内存地址指针,而是一个引用的拷贝。
以题中回溯代码为例:
public List<String> generateParenthesis(int n) {
List<String> list = new ArrayList<>(); // 在堆上创建 ArrayList 实例,栈中变量 list 指向它
backtrack(list, "", 0, 0, n); // 传入的是 list 引用的副本(值),非 list 本身
return list; // 返回的仍是原始 list 变量,其内容已被 backtrack 修改
}在 backtrack 方法中:
- list.add(str) 实际操作的是堆中那个唯一的 ArrayList 实例,因此外部 list 内容同步更新;
- 但若在 backtrack 中写 list = new ArrayList<>(),这只是让当前栈帧中的形参 list 指向一个新对象,原方法中的 list 变量仍指向旧的、已被修改的列表——这正是“传引用副本”的铁证。
✅ 正确理解模型:
立即学习“Java免费学习笔记(深入)”;
- 基本类型(int, boolean 等):传值 → 副本修改不影响原变量;
- 引用类型(List, String, 自定义类等):传的是“引用的值”(即内存地址的拷贝)→ 副本可操作同一堆对象,但不可改变调用方变量的指向。
⚠️ 注意事项:
- String 是不可变类(immutable),即使传入 String str,str += "x" 也会创建新对象,原引用不受影响(因 str 是副本,且赋值后指向新字符串);
- 若需彻底隔离对象修改,应显式克隆(如 new ArrayList<>(original))或使用不可变容器(如 List.copyOf());
- 静态变量、单例等全局状态与此机制无关——它们影响范围由作用域决定,而非参数传递方式。
总结:所谓“对象被修改”,本质是多个引用副本共同指向同一堆实例;Java 从未提供“传引用”语义。掌握这一底层模型,不仅能厘清 generateParenthesis 类回溯逻辑的正确性,更是安全设计多线程、避免意外副作用、理解集合框架行为的基石。










