之前用的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.對於物件,相當於複製了一個引用,該引用指向的還是原變數的記憶體區域。