Java String是按“引用”传递的吗?
20
08 月

Java String是按“引用”传递的吗?

这是Java的一个经典问题。许多类似的问题已经在stackoverflow上被问过,也有很多不正确/不完整的答案。如果你不想太多,那这个问题很简单。但如果你深入考虑,它可能非常混乱。

1. 一是有趣并令人困惑的代码段

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”.

2. 常见的困惑

在堆(heap)中,x存储指向“ab“的引用,所以,当x作为一个参数传给change()方法时,仍然指向堆中的“ab”:

string-pass-by-reference--650x247

因为Java是按值传递,x的值指向”ab“的引用。当方法change()被调用,它会创建一个新的“cd”的对象,而x现在指向“cd”:

string-pass-by-reference-2-650x247

这似乎是一个非常合理的解释。他们明确表示,Java是总是按值传递。但是,这里有什么问题呢?

3. 代码真正做了什么呢?

上面的解释有几个错误。为了更容易理解这一点,我们来简单地梳理整个过程。

当创建的字符串“ab”时,Java分配内存来存储字符串对象。然后,这个对象被分配到变量x,变量x被实际分配给对象一个引用,此引用是该对象在内存中存储时的地址。

变量x包含字符串对象的引用,但x本身并不是引用!它是存储引用(内存地址)的变量。

Java是只按值传递。当x被传递到的change()方法中时,x值(引用)的副本被传递。方法change()创建另一个对象“cd”,并有一个不同的引用。然后是变量x改变自身的引用(指向“cd”),而不是引用本身改变。

下图展示了真是做了什么:

string-pass-by-reference-3-650x244

4. 错误的解释

从第一代码段中提出的问题与字符串不变性是不相干的。即使字符串被StringBuilder替换,结果仍然一样。关键的一点是变量存储着引用,而不是引用本身!

5. 这个问题的解决方案

如果我们真的需要改变对象的值。首先,对象应该是可变的,例如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");
}