java中到底傳值仍是引用

JAVA中的傳遞都是值傳遞嗎?有沒有引用傳遞呢?

在回答這兩個問題前,讓咱們首先來看一段代碼:
Java代碼
  1. public class ParamTest {   
  2.     // 初始值爲0   
  3.     protected int num = 0;   
  4.   
  5.     // 爲方法參數從新賦值   
  6.     public void change(int i) {   
  7.          i = 5;   
  8.      }   
  9.   
  10.     // 爲方法參數從新賦值   
  11.     public void change(ParamTest t) {   
  12.          ParamTest tmp = new ParamTest();   
  13.          tmp.num = 9;   
  14.          t = tmp;   
  15.      }   
  16.   
  17.     // 改變方法參數的值   
  18.     public void add(int i) {   
  19.          i += 10;   
  20.      }   
  21.   
  22.     // 改變方法參數屬性的值   
  23.     public void add(ParamTest pt) {   
  24.          pt.num += 20;   
  25.      }   
  26.   
  27.     public static void main(String[] args) {   
  28.          ParamTest t = new ParamTest();   
  29.   
  30.          System.out.println("參數--基本類型");   
  31.          System.out.println("原有的值:" + t.num);   
  32.         // 爲基本類型參數從新賦值   
  33.          t.change(t.num);   
  34.          System.out.println("賦值以後:" + t.num);   
  35.         // 爲引用型參數從新賦值   
  36.          t.change(t);   
  37.          System.out.println("運算以後:" + t.num);   
  38.   
  39.          System.out.println();   
  40.   
  41.          t = new ParamTest();   
  42.          System.out.println("參數--引用類型");   
  43.          System.out.println("原有的值:" + t.num);   
  44.         // 改變基本類型參數的值   
  45.          t.add(t.num);   
  46.          System.out.println("賦引用後:" + t.num);   
  47.         // 改變引用類型參數所指向對象的屬性值   
  48.          t.add(t);   
  49.          System.out.println("改屬性後:" + t.num);   
  50.      }   
  51. }  
public class ParamTest {
// 初始值爲0
protected int num = 0;
// 爲方法參數從新賦值
public void change(int i) {
i = 5;
}
// 爲方法參數從新賦值
public void change(ParamTest t) {
ParamTest tmp = new ParamTest();
tmp.num = 9;
t = tmp;
}
// 改變方法參數的值
public void add(int i) {
i += 10;
}
// 改變方法參數屬性的值
public void add(ParamTest pt) {
pt.num += 20;
}
public static void main(String[] args) {
ParamTest t = new ParamTest();
System.out.println("參數--基本類型");
System.out.println("原有的值:" + t.num);
// 爲基本類型參數從新賦值
t.change(t.num);
System.out.println("賦值以後:" + t.num);
// 爲引用型參數從新賦值
t.change(t);
System.out.println("運算以後:" + t.num);
System.out.println();
t = new ParamTest();
System.out.println("參數--引用類型");
System.out.println("原有的值:" + t.num);
// 改變基本類型參數的值
t.add(t.num);
System.out.println("賦引用後:" + t.num);
// 改變引用類型參數所指向對象的屬性值
t.add(t);
System.out.println("改屬性後:" + t.num);
}
}

這段代碼的運行結果以下:
  1. 參數--基本類型
  2. 原有的值:0
  3. 賦值以後:0
  4. 運算以後:0

  5. 參數--引用類型
  6. 原有的值:0
  7. 賦引用後:0
  8. 改屬性後:20

從上面這個直觀的結果中咱們很容易得出以下結論:
  1. 對於基本類型,在方法體內對方法參數進行從新賦值,並不會改變原有變量的值。
  2. 對於引用類型,在方法體內對方法參數進行從新賦予引用,並不會改變原有變量所持有的引用。
  3. 方法體內對參數進行運算,不影響原有變量的值。
  4. 方法體內對參數所指向對象的屬性進行運算,將改變原有變量所指向對象的屬性值。

上面總結出來的不過是咱們所看到的表面現象。那麼,爲何會出現這樣的現象呢?這就要說到值傳遞和引用傳遞的概念了。這個問題向來是很有爭議的。

你們都知道,在JAVA中變量有如下兩種:
  1. 基本類型變量,包括char、byte、short、int、long、float、double、boolean。
  2. 引用類型變量,包括類、接口、數組(基本類型數組和對象數組)。

當基本類型的變量被看成參數傳遞給方法時,JAVA虛擬機所作的工做是把這個值拷貝了一份,而後把拷貝後的值傳遞到了方法的內部。所以在上面的例子中,咱們回頭來看看這個方法:
Java代碼
  1. // 爲方法參數從新賦值   
  2. public void change(int i) {   
  3.      i = 5;   
  4. }  
// 爲方法參數從新賦值
public void change(int i) {
i = 5;
}

在這個方法被調用時,變量i和ParamTest型對象t的屬性num具備相同的值,倒是兩個不一樣變量。變量i是由JAVA虛擬機建立的做用域在 change(int i)方法內的局部變量,在這個方法執行完畢後,它的生命週期就結束了。在JAVA虛擬機中,它們是以相似以下的方式存儲的:

很明顯,在基本類型被做爲參數傳遞給方式時,是值傳遞,在整個過程當中根本沒有牽扯到引用這個概念。這也是你們所公認的。對於布爾型變量固然也是如此,請看下面的例子:
Java代碼
  1. public class BooleanTest {   
  2.     // 布爾型值   
  3.     boolean bool = true;   
  4.   
  5.     // 爲布爾型參數從新賦值   
  6.     public void change(boolean b) {   
  7.          b = false;   
  8.      }   
  9.   
  10.     // 對布爾型參數進行運算   
  11.     public void calculate(boolean b) {   
  12.          b = b && false;   
  13.         // 爲了方便對比,將運算結果輸出   
  14.          System.out.println("b運算後的值:" + b);   
  15.      }   
  16.   
  17.     public static void main(String[] args) {   
  18.          BooleanTest t = new BooleanTest();   
  19.   
  20.          System.out.println("參數--布爾型");   
  21.          System.out.println("原有的值:" + t.bool);   
  22.         // 爲布爾型參數從新賦值   
  23.          t.change(t.bool);   
  24.          System.out.println("賦值以後:" + t.bool);   
  25.   
  26.         // 改變布爾型參數的值   
  27.          t.calculate(t.bool);   
  28.          System.out.println("運算以後:" + t.bool);   
  29.      }   
  30. }  
public class BooleanTest {
// 布爾型值
boolean bool = true;
// 爲布爾型參數從新賦值
public void change(boolean b) {
b = false;
}
// 對布爾型參數進行運算
public void calculate(boolean b) {
b = b && false;
// 爲了方便對比,將運算結果輸出
System.out.println("b運算後的值:" + b);
}
public static void main(String[] args) {
BooleanTest t = new BooleanTest();
System.out.println("參數--布爾型");
System.out.println("原有的值:" + t.bool);
// 爲布爾型參數從新賦值
t.change(t.bool);
System.out.println("賦值以後:" + t.bool);
// 改變布爾型參數的值
t.calculate(t.bool);
System.out.println("運算以後:" + t.bool);
}
}

輸出結果以下:
  1. 參數--布爾型
  2. 原有的值:true
  3. 賦值以後:true
  4. b運算後的值:false
  5. 運算以後:true

那麼當引用型變量被看成參數傳遞給方法時JAVA虛擬機又是怎樣處理的呢?一樣,它會拷貝一份這個變量所持有的引用,而後把它傳遞給JAVA虛擬機爲方法 建立的局部變量,從而這兩個變量指向了同一個對象。在篇首所舉的示例中,ParamTest類型變量t和局部變量pt在JAVA虛擬機中是以以下的方式存 儲的:

有一種說法是當一個對象或引用類型變量被看成參數傳遞時,也是值傳遞,這個值就是對象的引用,所以JAVA中只有值傳遞,沒有引用傳遞。還有一種說法是引 用能夠看做是對象的別名,當對象被看成參數傳遞給方法時,傳遞的是對象的引用,所以是引用傳遞。這兩種觀點各有支持者,可是前一種觀點被絕大多數人所接 受,其中有《Core Java》一書的做者,以及JAVA的創造者James Gosling,而《Thinking in Java》一書的做者Bruce Eckel則站在了中立的立場上。

我我的認爲值傳遞中的值指的是基本類型的數值,即便對於布爾型,雖然它的表現形式爲true和false,可是在棧中,它仍然是以數值形式保存的,即0表 示false,其它數值表示true。而引用是咱們用來操做對象的工具,它包含了對象在堆中保存地址的信息。即便在被做爲參數傳遞給方法時,實際上傳遞的 是它的拷貝,但那還是引用。所以,用引用傳遞來區別與值傳遞,概念上更加清晰。

最後咱們得出以下的結論:
  1. 基本類型和基本類型變量被看成參數傳遞給方法時,是值傳遞。在方法實體中,沒法給原變量從新賦值,也沒法改變它的值。
  2. 對象和引用型變量被看成參數傳遞給方法時,在方法實體中,沒法給原變量從新賦值,可是能夠改變它所指向對象的屬性。至於到底它是值傳遞仍是引用傳遞,這並不重要,重要的是咱們要清楚當一個引用被做爲參數傳遞給一個方法時,在這個方法體內會發生什麼。

什麼叫引用?只由於這個變量的值和其它的不同.


首先理解:都是變量
int i;
ArrayList b;
i和b都是變量.
但i是基本變量,也叫原始變量.
其它的就叫引用變量,由於它的值是一個內存地址值.引用對象的.但記住:它們都是有一個值的!i是一個數字,而b是一個內存地址值(簡單的說是一個十六進 制的值).除了基本變量以外的變量都是引用變量.Vector a;這裏的a也是一個變量.它也是有值的,它的值是一個十六進制的值.

變量的賦值:
int i=10;
int j=i;
//這裏把i的值10給了j,因此j的值也是10

ArrayList b=new ArrayList();
ArrayList c=b;
//首先,b是一個引用變量,它的"值":是一個內存地址值!!! new ArrayList()要分配一段內存保存它們,怎麼樣找到這段內存?那就是經過b裏的值了.b的值就是new ArrayList()所佔內存的首地址.而後c也是一個引用變量,它的值(地址值)和b是同樣的.也就是new ArrayList()所佔內存的首地址.因此當經過b或者c進行操做時,它們都是操做同一個對象的.

在方法調用的時候,方法的參數實際也就是一個變量.若是是基本類型變量的時候,假設有方法method(int aa);
int j=10;
method(j);
這裏邊,int aa實際也是定義了一個變量,調用的時候把j的值:10也給了aa.因此aa也是10,改變了aa的值並不會改變j的值.

若是是引用變量的時候,假設有方法methodA(ArrayList aa);
ArrayList b = new ArrayList();
methodA(b);
//方法定義了變量aa,調用的時候把b的值(地址值!!!!!)給了aa,因此aa與b有同樣的值(地址值!!!!),在方法裏經過aa去操做的時候,b所引用的對象也就被改變了,由於它們引用同一個對象.

紙 a = new 銀行賬戶();//開一個銀行賬戶,返回一個卡號給你,寫在你的紙a裏邊.

用一張紙(引用變量),把你的銀行卡號寫在上邊,而後調用個人時候,我用另一張紙(引用變量---方法的形數),把你的號碼抄過來.而後我經過這個卡號,去到銀行找到你的賬號,給你存點錢.

而後你用你的紙(引用變量)上的卡號 <沒變,仍是那個卡號>再去查詢銀行賬號的時候就會發現了多了一些錢了.....java

說說我對值傳遞和引用傳遞的見解:
首先我認爲,你們對Java傳遞參數的行爲是清楚的,這個爭論只是一個語義上的爭論。
也就是咱們是否須要區分值傳遞和應用傳遞呢?或者說這樣的區分有沒有意義?是否合理?

博主認爲存在引用傳遞的關鍵點在於,傳遞的對象地址值,本質上它是一個引用,不管它是否被copy過。
認爲只有值傳遞的關鍵點在於,傳遞的對象地址值,它是一個值的copy,這個值表明的意義無所謂。

引用是c++裏的概念,因爲java跟c++是有必定關係的,這裏把引用遷移過來,若是合理何嘗不可。
c++中關於引用的解釋通常喜歡說是看做「別名」,我查了幾本書,大部分提到引用並不會分配內存空間,也有一本書提到,某些編譯器會分配存儲空間來存儲被引用對象的地址。
那麼仍是回到語義上來,c++裏的這個引用,語義上是「別名」的意思,個人理解是,一組指向同一個對象的別名應該只存儲一分內存地址。固然具體實現可能會 把引用當作一個不可變的指針來處理(每一個別名都存儲本身的對象地址)。可是請注意,咱們應該關注於它的語義,即:它沒有任何值的copy,即便是一個地 址,只是另一個名字而已。

可是java裏面沒有這樣的概念,全部的地址傳遞其行爲是值的傳遞方式,語義上統一成值傳遞更爲清晰,咱們只須要考慮這個值具體是什麼,無非兩種,要麼是基本類型值,要麼是個地址。
因此我認爲這個「引用」的概念放到java中並不合適。只有值傳遞的說法更合理。c++

相關文章
相關標籤/搜索