1. 程式人生 > >JAVA 方法的引數是按值傳遞還是引用傳遞?

JAVA 方法的引數是按值傳遞還是引用傳遞?

  • 在思考這個問題之前首先要明白JAVA一個類中的資料成員有多少種:
                    JAVA一個類中資料成員只有兩種,分別是:基本資料型別和物件。基本資料型別就不用多說了,只有8種;其他的都是物件,JAVA class位元組碼檔案在記憶體中是一個靜態物件、類例項是物件,比如String物件。你可能會覺得奇怪,陣列呢?特別是基本資料型別的陣列呢?在這裡,你要明白,JAVA中陣列也是物件,基本型別陣列是一個由JVM建立的物件,其他陣列,比如Array類等,本身就是類,肯定是物件。
  • 作用域,在JAVA中沒有作用域的概念,一般描述方法的作用範圍是左花括號“{”與右花口號之間“}”。作用域在C++中是一個重要的概念,是類、物件、變數的影響範圍和訪問許可權的描述。比如類中方法的作用域:在方法中可以訪問類的所有有許可權訪問的變數和其他方法,但是不能訪問其他方法內的變數;另外類的變數能被該方法內訪問,但是卻不能訪問其他方法內的變數。
  • 明白JAVA一個類中的資料成員有多少種有什麼用呢?
                    目的是知道JAVA引數傳遞的種類,通過上面可以知道,JAVA方法引數只能傳遞兩種資料成員:基本資料型別和物件。                    (如果學過C++可以思考一下C++函式引數傳遞的資料成員有幾種?這樣方便區分概念,不思考清楚,很容易被C++弄混自己對JAVA的理解的。我個人理解C++函式引數傳遞有4種:基本資料型別、物件、指標、地址)
  • 要理解標題,還要弄清楚兩個概念:按值傳遞是什麼?引用傳遞是什麼?
  1. 按值傳遞:函式方法的引數即形參是實際引數的一個拷貝
                    
  1. 引用傳遞:指在呼叫函式時將實際引數的地址傳遞到函式中,那麼在函式中對引數所進行的修改,將影響到實際引數。(來源百度百科)
                              (在C++中可能會有更加複雜的引用關係,不過不是本文討論內容)
  • JAVA方法的引數是按值傳遞還是引用傳遞?
                   通過上面我們知道JAVA一個類中只有兩種資料成員:基本資料型別和物件。基本資料型別不用討論了,在JAVA肯定是按值傳遞(當然在棧記憶體中的實際情況更加複雜,這裡只談表象。C++可以把基本資料型別任意傳,按值、引用、指標和地址,全套服務都可以)                  那麼我們只需要討論傳物件了。首先,我們需要明白物件在記憶體的建立位置,和建立模式。JAVA物件建立有兩種方式,一種是在堆記憶體中建立,另一個是在棧記憶體中建立。堆中建立的物件是個完整的物件,物件中包含所有應該有的資料。而棧中建立,比較小(可能僅僅是該物件型別的一個變數名記憶體空間,即只能儲存物件的地址,和其他一些少量資料(猜的,不對別打我= = JAVA那坑爹設計,根本獲取不了具體堆和棧的地址和大小。聽說通過外部工具可以,也麻煩得要死。為什麼JAVA不允許除錯模式檢視或者獲取記憶體地址和大小呢)。
  1. 堆物件記憶體:比較大,包含所有該物件應該有的資料。
  2. 棧物件記憶體:比較小,其他不確定。但是隻要明白,一般不能確定大小的物件(可變大小物件,比如可變長度的陣列,類物件)都是在堆中建立的就可以了。new操作就是在堆中建立記憶體的。
                 以 String str=new String("Hello")為例,這句語句實際上會建立兩個物件,一個堆物件,一個棧物件。堆中的String物件變數名不是str,實際名字只有JVM才知道,就假設以堆的記憶體地址作為名字吧。而棧中的String物件名才叫做str                                       傳遞到方法中void setString(String str2)後的記憶體變化                                         str2是setString方法的形參,str是外部實參,由上面的作用域分析知道setString的str2變數必須拷貝實參str的內容到形參str2上才能訪問,str的內容是堆地址0xFF01,所以方法就可以通過形參訪問堆中的Hello字串。               從這裡就可以看出JAVA方法的引數是按值傳遞的,不是嗎?。不過可能還不太相信是吧。再想想按值傳遞的概念吧,按值傳遞:函式的形參引數即形參是實參的一個拷貝。假設堆中的Hello字串記憶體名字為它的地址,那麼這裡的實參是什麼?是0xFF01嗎?錯!!是棧中的str記憶體!!形參拷貝的是棧中str的內容,而str記憶體的內容是0xFF01,即堆記憶體地址,那麼形參的內容就是堆的記憶體0xFF01。想得快得人應該會立刻想到,既然str2記憶體中的內容是堆記憶體0xFF01,那不就是引用咯?是個蛋蛋,這裡問題是JAVA方法的引數是按值傳遞還是引用傳遞,是傳遞過程中的實參和形參之間的問題,關實參、形參記憶體中的內容個屁事……即與實參、形參內容無關!!              那為什麼有些書會說是引用傳遞?比如JAVA程式設計思想中文版第4版就這樣說。因為這些書說的是最終效果,而不是過程。最終效果的確是引用傳遞,這樣才能讓方法中對物件屬性進行修改響應到實際記憶體中。說成引用傳遞也是為了讓新人理解方法為什麼不用返回值,就可以通過引數傳遞進來的資料進行更改外面的資料。由於當時寫這個文章小弟才疏學淺,所以解釋有點亂,補充一個知乎上比較可靠的說法,方便理解:https://www.zhihu.com/question/31203609