原文:String is passed by “reference” in Java
这是Java中的一个经典问题,在Stack Overflow上有许多相似的问题,有些是错误的或并不完整的回答。如果你不考虑许多情况这问题是简单的。但如果你考虑更多的情况,它可能是令人困惑的。
一个令人感兴趣且令人困惑的代码片段
public static void main(String[] args) {
String x = new String("ab");
change(x);
System.out.println(x);
}
public static void change(String x) {
x = "cd";
}
程序打印结果为:ab
在C++中,代码如下:
void change(string &x) {
x = "cd";
}
int main(){
string x = "ab";
change(x);
cout << x << endl;
}
程序打印结果为:cd
常见的令人困惑的问题
变量x
中存储着指向堆中字符串ab
的引用,当变量x
作为change()
调用时的参数时,它仍然指向堆中的字符串ab
,如图:
因Java中是值传递,变量x
的值为字符串ab
的引用。当change()
被调用时,它创建一个新对象cd
,使变量x
指向cd
这似乎是一个相当合理的解释。这是明显的-Java总是值传递。但这里什么是错误的呢?
这段代码实际做了什么?
这解释显然有些错误,为了很容易理解这一点,在整个过程中简单地走一走是个好主意。
当字符串ab
被创建,Java分配必要的内存空间存储字符串对象。然后,这对象被分配给到变量x
,实际上变量x
被指定为对象的引用。这引用是这个对象被保存在内存中的地址。
变量x
包含一个指向字符串对象的引用, 变量x
不是它自身的引用。它是一个存储内存地址引用的变量。
Java仅支持值传递。当变量x
被传递到change()
中时,传递的是变量x
的值的副本。change()
创建另一个对象cd
,它有一个不同的引用。变量x
改变它的引用指向cd
,而不是引用本身。
错误的解释
第一个代码片段中提出的问题与字符串是不可变的无任何关系。即使将String
替换为StringBuilder
,这结果也是相似的。
这关键点是这变量存储的是引用,而不是引用本身。
解决问题
如果我们真的需要改变对象的值。首先,这对象应该是可改变的,例如:StringBuilder
。第二,我们需要确保这里没有新对象被创建并被指定到参数变量上,因为Java仅支持值传递。
public static void main(String[] args) {
StringBuilder x = new StringBuilder("ab");
change(x);
System.out.println(x);
}
public static void change(StringBuilder x) {
x.delete(0, 2).append("cd");
}
这篇文章的重点不在于值传递
与引用传递
之争,而是理解清楚第一段代码中,程序实际上做了哪些操作?是如何操作的?