問題源於在Java中使用對象作為參數,按照C++的思路進行調試,發現結果與C++中並不相同。
導致該問題的原因是Java與C++對於對象的解釋是不相同的。
在C++中對象作為參數采用的是“傳值調用”,當實參通過形參傳遞時,會調用對象(實參)的拷貝構造函數(如果沒有顯式的定義拷貝構造函數,將自動調用默認拷貝構造函數,它的功能是將實參中的對象原樣的拷貝到形參中,這里牽扯到深拷貝和淺拷貝的問題,但不影響對本問題的分析。),函數實際操作的是該對象的拷貝,並不影響原對象。
而在Java中,對象作為參數時,形參是被初始化為實參對象的引用。如果對形參進行操作會影響到實參(原對象)。這有點類似於C++中的”引用調用“,但並非如此,Java中采用的仍然是”傳值調用“。而導致結果不同的原因是,Java與C++中對對象名的解釋是不同的。在C++中“類”和“對象”的關系可以類比為“類型”和“變量”之間的關系,對象在定義的時候即分配了內存空間,它是實實在在存在的。而在Java中,對象定義時只是定義了“對象變量”,它只是指向一個對象,當使用new操作符時才會構造指向的這個對象。Java中的對象名有點像C++中指向對象的指針,當作為參數傳遞時,傳遞的是該對象在內存中的地址。
在C++和Java中String類的對象str的存儲方式分別如下:
以Java中對象作為參數傳遞的例子分析一下:
1 public class Test1 { 2 public static void main(String[] args) { 3 StringBuffer str = new StringBuffer("Hello "); 4 System.out.println("Before change, str = " + str); 5 changeData(str); 6 System.out.println("After changeData, str = " + str); 7 } 8 9 public static void changeData(StringBuffer strBuf) { 10 strBuf.append("World!"); 11 } 12 }
按照上面的分析,將str傳遞給strBuf 時,是將Hello存儲的地址傳遞過去,那么輸出結果是:
Before change, str = Hello
After changeData, str = Hello World!
將上面的代碼修改一下,如下:
1 public class Test2 { 2 public static void main(String[] args) { 3 StringBuffer str = new StringBuffer("Hello "); 4 System.out.println("Before change, str = " + str); 5 changeData(str); 6 System.out.println("After changeData, str = " + str); 7 } 8
9 public static void changeData(StringBuffer strBuf) { 10 strBuf = new StringBuffer("Hi "); 11 strBuf.append("World!"); 12 } 13 }
按照上面的分析,將str傳遞給strBuf 時,strBuf 與str都將存放Hello的存儲地址,如下圖:
然后當執行完 strBuf = new StringBuffer("Hi "); 后,以上關系將變成:
此時strBuf 中存放的不再是Hello的地址,而是Hi的地址,new操作符操作成功后總會在內存中新開辟一個存儲區域。
當執行 strBuf.append("World!"); 這句時,此時操作的將是Hi,而不是Hello,那么結果將變成:
最后輸出結果如下:
Before change, str = Hello
After changeData(n), str = Hello