"Java 中只有按值傳遞",做爲初學者初看到這幾個字有點不敢相信,無數次經過函數改變過對象,無數次跟同事說 Java 在傳對象的時候是按引用傳遞。後來細細想一想,之因此覺得 Java 傳對象是按引用傳遞是由於其中有不少概念都沒理清楚,與 C++ 中的搞混了。從 C++ 轉 Java 的時候將 C++ 中的知識點映射到 Java 沒錯,這有利於 C++ 轉 Java 的人更快的學習 Java。但一旦映射錯誤就很容易造成固定思惟。java
在 C++ 和 Java 中都有引用的概念,但他們徹底不是同一個東西。Java 中的引用更相似 C++ 的指針,C++ 的引用在 Java 中並沒有對應概念。在 C++ 中有按值傳遞、按指針傳遞和按引用傳遞三種,而在 Java 中沒有 C++ 引用和指針的概念,只保留了按值傳遞。程序員
爲了更好的說明 Java 中只有按值傳遞,先來看看 Java 的數據類型,Java 的數據類型分爲基本數據類型和引用類型,其中:架構
基本類型包括 byte/short/int/long/float/double/char/boolean 八種,基本類型在內存中地址中保存的即自己的值,其通常都在棧上分配。函數
引用類型指向一個對象,它與 C++ 的指針很是類似。但 C++ 的指針能夠指向基本類型和類對象,而 Java 的引用只能指向類(枚舉、接口等)對象。Java 中對象自己在堆上分配,而引用類型在棧上分配,其內存地址中保存的是對象在堆中的地址。兩種類型在內存中的佈局以下:佈局
1 class MyInteger {
2 int value;
3 }
4
5 public class TestReference {
6
7 public static void changeBasic(int arg) {
8 arg = 2;
9 }
10
11 public static void changeReference(MyInteger arg) {
12 arg.value = 2;
13 }
14
15 public static void main(String[] args) {
16
17 int basicTypeA = 1;
18
19 MyInteger referenceTypeA = new MyInteger();
20 referenceTypeA.value = 1;
21
22 System.out.println("調用 changeBasic 以前 basicTypeA 的值 "+ basicTypeA);
23 changeBasic( basicTypeA);
24 System.out.println("調用 changeBasic 以後 basicTypeA 的值 "+ basicTypeA);
25
26 System.out.println("調用 changeReference 以前 referenceTypeA 的值 "+ referenceTypeA.value);
27 changeReference( referenceTypeA);
28 System.out.println("調用 changeReference 以後 referenceTypeA 的值 "+ referenceTypeA.value);
29 }
30 }
複製代碼
運行結果以下:性能
能夠看出基本類型 int 的變量 basicTypeA 在 changeBasic 調用後值並無發生改變,而引用類型 MyInteger 的變量 referenceTypeA 在調用 changeReference 後發生了改變。這裏就比較容易誤導讀者覺得:Java 基本類型是按值傳遞而引用類型是按引用傳遞(暫且這麼定義)。其實否則,按值傳遞的意思想必你們都知道:傳遞的是值的的拷貝,好比上面代碼中的調用 changeBasic(basicTypeA) 時,arg 是 basicTypeA 的一個拷貝,因此不管對 arg 作任何操做都不影響 basicTypeA 變量自己。而調用 changeReference(referenceTypeA) ,arg 也是 referenceTypeA 的一個拷貝,可是因爲 arg 和 referenceTypeA 都是引用類型且他們指向同一對象,因此經過 arg 修改對象,referenceTypeA 也能看到。兩種類型變量在內存中調用過程以下:學習
因此能夠看出不管是基本類型仍是引用類型,都是按值傳遞。只是因爲它們在內存中所表示的內容不一樣,最後表現出來的結果也有所不一樣。同理,在 C++ 中的按值傳遞、按指針傳遞和按引用傳遞理論上均可以歸爲按值傳遞(其實這個歸類在學 C++ 的時候就概括出來了,只是後來反而忘了)。spa
Java 的引用相似於 C++ 的指針,可是 C++ 的對象(不包括基本類型)傳遞提供了對象自己直接傳遞和指針傳遞兩種方式(引用方式不談),而 Java 對象只有對引用進行傳遞這一種,不存在直接將對象自己進行傳遞。設計
在 C++ 中以上兩種傳遞方式能夠自行選擇,而 Java 裏面只有第 2 種方式,凡事有利有弊,有時候咱們並不想在函數中改變原對象的內容,這裏我就踩過一個坑,咱們的項目中有個對象經過管道傳遞的流程以下:3d
funcA 與 funcB 是兩個不一樣的人負責的,一次升級後 funcB 的負責人發如今函數中獲取的對象 X 內容不對,一開始還覺得是傳遞對象 X 的接口出現了錯誤即是一頓排查,知道最後才發現對象 X 是在升級後在 funcA 中被修改了,浪費了很多時間。固然這個架構的流程設計的不合理是主要緣由(只須要在分發的時候講對象 X 作手動拷貝便可避免上述問題),可是不不影響咱們拋出 Java 只能對引用進行傳遞的弊端。
在調用鏈較長、各類 for/while 循環中很容易就犯了上述錯誤,解決方案固然就是手動拷貝對象,Java 中拷貝對象有如下兩種方式:
其中第二種方式能避免深淺拷貝的問題,但調用比較耗時。第一種也能避免深淺拷貝可是須要本身手動去寫相應的代碼,若是嵌套較深,代碼將很是複雜。至於深淺拷貝的問題能夠自行百度,其本質仍是由於只是將對象的引用進行了傳遞而致使的一些問題。記得關注公衆號哦,記錄着一個 C++ 程序員轉 Java 的學習之路。