This is a classic question of Java. Many similar questions have been asked on stackoverflow, and there are a lot of incorrect/incomplete answers. The question is simple
if you don’t think too much. But it could be very confusing, if you give more thought
to it.
這是一個非常經(jīng)典的問題,許多類似的問題在stackoverflow上被提問,有很多不正確或者不完整的回答。如果你不考慮那么多,直接認為string是immutable的,那問題就很簡單,如果你想要了解更多細節(jié),問題就變的很復(fù)雜。
我們看下面這個非常容易混淆,同時又很經(jīng)典的代碼
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";
}
我們可能以為會輸出cd,實際上輸出的是ab
這是一個很容易混淆的問題,我們來看一下一個貌似很合理的解釋:
x stores the reference which points to the "ab" string in the heap. So when x is passed
as a parameter to the change() method, it still points to the "ab" in the heap like the
following:
變量x存儲的是引用,這個引用指向堆上的string“ab”,如下圖所示:
所以x傳遞給change方法的x參數(shù),然后新建一個string“cd”,然后x有指向這個新建的cd變量
這樣的解釋看上去一點問題沒有,但為什么輸出的結(jié)果又不對呢?
真正的代碼執(zhí)行過程應(yīng)該是這樣的:
When the string "ab" is created, Java allocates the amount of memory required to
store the string object. Then, the object is assigned to variable x, the variable is actually
assigned a reference to the object. This reference is the address of the memory location
where the object is stored.
當(dāng)string變量‘a(chǎn)b’被創(chuàng)建出來的之后,java分配一塊足夠大小的內(nèi)存去存儲這個string對象,這個對象被分配給變量x,這個變量x實際上存儲的是這個對象在內(nèi)存中的地址。
The variable x contains a reference to the string object. x is not a reference itself! It
is a variable that stores a reference(memory address).
Java is pass-by-value ONLY. When x is passed to the change() method, a copy of
value of x (a reference) is passed. The method change() creates another object "cd" and
it has a different reference. It is the variable x that changes its reference(to "cd"), not
the reference itself.
這個變量x包含一個這個string對象的一個引用。切記 ** x不是引用本身 **,x只是一個變量存儲了一個引用(這個引用其實就是內(nèi)存的地址)。
java只通過value傳遞當(dāng)x被傳遞給change方法的時候。會將x的一份拷貝傳遞給change方法中的局部變量x,這是另外一個x,雖然這個x存儲的引用也就是地址的值是一樣的,待會就被改變了,change方法新建一個對象“cd”,是局部變量里的x指向這個新建cd,所以原本的x還是指向ab。
我們可以測試其他引用類型的傳遞,會發(fā)現(xiàn)他們實際上都是通過值傳遞的,會在方法里新建一個引用,當(dāng)我們對這個引用指向一個新對象時就要注意了
import java.util.ArrayList;
import java.util.List;
public class StringTest {
public static void main(String[] args) {
String x = new String("ab");
change(x);
System.out.println(x);
Integer y = new Integer(5);
change(y);
System.out.println(y);
List<Integer> a = new ArrayList<Integer>();
a.add(1);
a.add(4);
change(a);
System.out.println(a);
}
public static void change(String x) {
x = "cd";
}
public static void change(Integer y) {
y = 4;
}
public static void change(List<Integer> a) {
//a.add(5);
a = new ArrayList<Integer>();
a.add(2);
}
}
輸出結(jié)果
因為我們在方法中都是新建一個對象,所以局部變量的引用都改變了,無法改變原有的值,所以我們看到三個change方法都沒有起到作用。
當(dāng)我們向方法參數(shù)傳遞一個引用的時候要記住是傳遞的引用的值,而不是引用本身,當(dāng)我們不讓這個引用指向一個新對象的時候,不會出現(xiàn)問題,當(dāng)我們在方法中將局部的引用賦給一個new出來的對象,那么我們要切記,這時候這個引用已經(jīng)指向另一個對象了,它所操作的都不會反映在原有的對象上。
那么我們?nèi)绾谓鉀Q上面那個問題呢?
其實很簡單,只要不在方法里新建一個對象就行了。保持方法中的那個局部變量的引用也在原有對象上操作
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");
}
我們總結(jié)一個關(guān)鍵的問題,Java中沒有真正的按引用傳遞,所有變量都是按值value傳遞的,引用也是變量,只不過它的值是存的對象的地址。所以引用類型的變量在參數(shù)的傳遞過程中,也會新建一個局部變量,局部變量會得到和引用變量一樣的值,也就是指向同一個對象。