在 Java 中,“引用传递” 的问题经常让初学者困惑,因为 Java 本质上只有值传递(Pass by Value),但对于对象类型,传递的是引用的副本,而不是引用本身。这可能导致一些看似“引用传递失败”的情况。
1. Java 是值传递(Pass by Value)
- 基本类型(int, double, char...):直接传递值的副本,修改不影响原变量。
- 对象类型(String, 自定义类...):传递的是引用的副本(即对象内存地址的副本),修改对象属性会影响原对象,但重新赋值不会影响原引用。
示例 1:基本类型(值传递)
public class Main {
public static void modify(int x) {
x = 20; // 修改的是副本,不影响原变量
}
public static void main(String[] args) {
int a = 10;
modify(a);
System.out.println(a); // 输出 10(未改变)
}
}
结论:基本类型是值传递,方法内修改不影响原变量。
示例 2:对象类型(传递引用的副本)
class Person {
String name;
Person(String name) { this.name = name; }
}
public class Main {
public static void modify(Person p) {
p.name = "Bob"; // 修改对象属性,会影响原对象
}
public static void reassign(Person p) {
p = new Person("Alice"); // 重新赋值,不影响原引用
}
public static void main(String[] args) {
Person person = new Person("Tom");
modify(person);
System.out.println(person.name); // 输出 "Bob"(修改成功)
reassign(person);
System.out.println(person.name); // 仍然输出 "Bob"(重新赋值失败)
}
}
结论:
- 修改对象属性(
p.name = "Bob")会影响原对象,因为传递的是引用的副本,但指向的是同一个对象。 - 重新赋值引用(
p = new Person("Alice"))不会影响原引用,因为方法内部只是修改了副本的指向。
2. 为什么“引用传递”似乎失败了?
情况 1:方法内重新赋值,但外部未改变
public static void changeString(String str) {
str = "New Value"; // String 是不可变类,相当于 new String("New Value")
}
public static void main(String[] args) {
String s = "Hello";
changeString(s);
System.out.println(s); // 仍然输出 "Hello"
}
原因:String 是不可变类,str = "New Value" 实际上是让 str 指向了一个新对象,但原引用 s 不变。
情况 2:方法内 new 了一个新对象
public static void changeArray(int[] arr) {
arr = new int[]{100, 200}; // 重新赋值,不影响原引用
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
changeArray(nums);
System.out.println(nums[0]); // 仍然输出 1
}
原因:arr = new int[]{...} 让方法内的 arr 指向了新数组,但原 nums 仍然指向旧数组。
3. 如何让“引用传递”生效?
如果希望方法内部修改能影响外部变量,可以:
- 返回新对象并赋值(推荐):
public static Person reassignAndReturn(Person p) { return new Person("Alice"); } public static void main(String[] args) { Person person = new Person("Tom"); person = reassignAndReturn(person); // 重新赋值 System.out.println(person.name); // 输出 "Alice" } - 使用包装类(如
AtomicReference):import java.util.concurrent.atomic.AtomicReference; public static void change(AtomicReference<String> ref) { ref.set("New Value"); } public static void main(String[] args) { AtomicReference<String> strRef = new AtomicReference<>("Hello"); change(strRef); System.out.println(strRef.get()); // 输出 "New Value" } - 使用数组或容器(如
String[]):public static void change(String[] arr) { arr[0] = "New Value"; } public static void main(String[] args) { String[] s = {"Hello"}; change(s); System.out.println(s[0]); // 输出 "New Value" }
4. 总结
| 情况 | 是否影响原变量 | 原因 |
|---|---|---|
修改对象属性(obj.field = x) | ✅ 影响 | 引用副本仍指向原对象 |
重新赋值引用(obj = new Obj()) | ❌ 不影响 | 修改的是副本的指向 |
基本类型(int a) | ❌ 不影响 | 纯值传递 |
String(不可变类) | ❌ 不影响 | 每次修改相当于 new String() |
关键点:
- Java 只有值传递,对象类型传递的是引用的副本。
- 方法内部修改对象属性会影响原对象,但重新赋值引用不会影响原变量。
- 如果需要方法内部修改影响外部变量,可以返回新对象或使用包装类(如
AtomicReference)。
希望这个解释能帮你彻底理解 Java 的“引用传递”问题! 🚀