之前用的c/c++比較多,在c/c++中對於傳參類型,無外乎就是傳值、傳引用、傳指針這幾種。但在java中,由於沒有指針類型,其傳參的方式也發生了相應的變化。根據網上的信息,按我之前的理解,java中傳參方式應該主要有兩種:傳值和傳引用
一 傳值
java中的傳值即傳遞的是原變量的一個副本。基本的數據類型,如:char,int,double等類型的變量向函數傳遞的參數,都是變量的副本都屬於傳值。函數內部不論進行任何操作,都不影響變量本身,影響的只是變量的副本。
例如:
public class Test { public static void main(String[] args){ int a = 1, b = 2; fun(a,b); System.out.println("a="+a); } static void fun(int m , int n) { m = m + n; } }
運行結果為:a = 1. a的值並沒有發生變化, 說明此處的傳參是傳的值。
二 傳引用
從表面上來看,java中的對象類型都是傳遞的引用,而不是傳遞的值。首先,來看一個例子(此處以LinkedList對象為例):
public class Test { public static void main(String[] args){ List<String> str_1 = new LinkedList<String>(); str_1.add("111"); fun(str_1); System.out.println("str_1 ="+str_1); } static void fun(List s) { s.add("222"); } }
運行結果為: str_1 =[111, 222] . 從這個例子來看的話,java中兌現的傳參應該是傳的引用。那么,再看下面的一個例子:
public class Test { public static void main(String[] args){ List<String> str_1 = new LinkedList<String>(); List<String> str_2 = new LinkedList<String>(); str_1.add("111"); str_2.add("333"); fun(str_1,str_2); System.out.println("str_1 ="+str_1); System.out.println("str_2 ="+str_2); } static void fun(List s,List m) { List<String> str = new LinkedList<String>(); str = s; s = m; m = str; } }
運行結果為:str_1 =[111] , str_2 =[333] .我們發現str_1與str_2的值並沒有發生交換。 如果該對象傳的是引用的話,那它們兩個的值應該發生了變化才對。
然而,java中對象作為實參,傳遞的實際上還是值,即java中的傳參傳遞的都是值!為什么會這么說?我會從jvm的構成的角度來解釋一下自己對這個問題的理解。接下來我們先看一下JVM的結構:
三 JVM的結構及相應的解釋
JVM的結構圖如下:
從Jvm的結構圖上可以看出來,Jvm在實現的時候將屬於它的內存分為五部分,其中程序代碼(嚴格的說應當是字節碼)和基本的數據類型是放在java棧的棧幀中,而對象是從堆中分配的,堆這個東西我認為可以理解成“對象池”。程序和程序中需要用到的對象放在兩個相對獨立的區域中,那么程序怎么使用對象呢?實際上是程序中真正使用對象的地方其實只是聲明了一個對象的引用,也就是把堆中分配了的相應對象的地址放到引用中,當然引用也是放在棧內存中,棧和堆之間就是通過一個一個的引用來聯系的。至於引用,我們可以把它看做是一個指針常量,說白了,就是一個數值,這個數值所表達的是引用對象的地址。所以,不管是基本類型變量(int,float,double等)還是對象,相應的內存地址中存放的都是一個數(無符號整數,整數,浮點數等)。當方法調用時候,方法的參數會占內存中開辟一塊新的區域,同時把要傳遞的基本類型,或者引用類型名稱復制到這塊內存中,結果是,基本類型(存放在占內存中的)復制之后連同其數值也復制到了這塊內存中,而對象只是復制了引用名,實際還要聯系到原對象所在的堆區域中。之所以說java中傳遞的都是值,是因為傳遞得是內存地址中的數,當然,這個值對於基本類型和對象類型來說意義是不一樣的,對於基本類型這個數就是其值本身,傳遞值的結果就是,改變新的變量的值不影響舊的變量的值;而對於對象來說這個數是它的地址,傳遞這個值就相當於傳遞了真實對象的引用,傳遞了引用或者說是地址的結果就是變化會全局可見。所以說java中的方法都是按值調用的!只不過基本類型和對象類型的"值"的在具體的機制上作用不同。
所以在第二個例子中,fun方法中,兩個list s和m交換,實際只是str_1與str_2在棧中對應的引用值進行了交換,而引用與相應堆區域的聯系並沒有改變。因此,str_1與str_2的值並沒有發生變化。
綜上,java中不論是基本類型還是對象,方法調用時,傳遞的都是值!
1.對於基本類型的變量,相當於操作其副本。
2.對於對象,相當於復制了一個引用,該引用指向的還是原變量的內存區域。