java到底是按值傳遞還是按引用傳遞?

來源:互聯網
上載者:User

原帖:http://topic.csdn.net/t/20060605/13/4801113.html

 

(UnAgain)

 

最近看了一個文章,問“java到底是按值傳遞還是按引用傳遞?”。本來覺得很簡單,為了能說的準確一點,我還專門就這個問題看了看langspec3.0。一看收穫還真不小,就寫了這篇文章。

我還不敢確定自己的觀點對不對,所以貼在這裡,希望大家一起討論。

另外,貼在blog上了,在那裡的效果比這裡好。
http://blog.csdn.net/UnAgain/archive/2006/06/05/774039.aspx

1   資料類型
1.1   PrimitiveType(簡單類型)
1.2   ReferenceType(參考型別)
2.   變數
2.1   簡單類型變數
2.2   參考型別變數
3.賦值與傳遞
3.1   對象的賦值
3.2   傳遞
3.3   final變數能改變嗎?
3.4   封裝類的賦值與傳遞

1   資料類型
java的資料類型有兩類:
PrimitiveType(簡單類型)
ReferenceType(參考型別)

1.1   PrimitiveType(簡單類型)
(參考:langspec-3.0/typesValues.html#4.2)

PrimitiveType的分類如下所示:

PrimitiveType:
        NumericType
        boolean

NumericType:
        IntegralType
        FloatingPointType

IntegralType:   one   of
        byte   short   int   long   char

FloatingPointType:   one   of
        float   double

PrimitiveType是java預定義的類型,並且使用保留字命名。比如int、long、float等。由此看來其封裝類不算PrimitiveType。
1.2   ReferenceType(參考型別)
(參考:langspec-3.0/typesValues.html#4.3)
ReferenceType有三種類型:類、介面、和數組。

2.   變數
(參考:langspec-3.0/typesValues.html#4.12)
A   variable   is   a   storage   location   and   has   an   associated   type,   sometimes   called   its   compile-time   type,   that   is   either   a   primitive   type   (§4.2)   or   a   reference   type   (§4.3).
變數是關聯於特定類型的儲存單元,所關聯的類型有時叫做變數的編譯時間類型,即,既可以是簡單類型也可以是參考型別。
2.1   簡單類型變數
A   variable   of   a   primitive   type   always   holds   a   value   of   that   exact   primitive   type.
簡單類型的變數總是執持簡單類型的值。
2.2   參考型別變數
A   variable   of   a   class   type   T   can   hold   a   null   reference   or   a   reference   to   an   instance   of   class   T   or   of   any   class   that   is   a   subclass   of   T.   A   variable   of   an   interface   type   can   hold   a   null   reference   or   a   reference   to   any   instance   of   any   class   that   implements   the   interface.

類型是T的類的變數可以執持null引用,或者類T及其子類的執行個體引用。介面類型的變數可以執持null引用,或者任何實現該介面的類的執行個體引用。

註:與langspec2.0不同的是,3.0引入了泛型的概念,其中有Type   Variable的概念,上面的T就是一個Type   Variable。
3.賦值與傳遞
如上所述,可以得出下面結論:
1) 對於簡單類型變數的賦值是按值傳遞。就是說直接把數值存放到變數的儲存單元裡。
2) 對於參考型別的變數,賦值是把原對象的引用(可以理解為入口地址),存放在變數的儲存單元裡。
3.1   對象的賦值
簡單類型的賦值很容易理解,這裡僅討論對象的賦值。所有參考型別的執行個體就是我們常說的對象。
可以這樣說,除了null以外,任何變數的初始賦值都是分兩步:
1) 建立對象執行個體
2) 把對象執行個體的引用賦值給變數。

比如:
Object   o1   =   new   Object();
3.2   傳遞
傳遞是通過變數之間的賦值實現的。在以前的回貼中我說過這樣一句話,單純從變數的角度看,變數之間的賦值是值傳遞。現在我解釋一下我的觀點。

先舉一個例子:
//   java中所有的類的基類預設為Object,在此不贅述。
class   Object1   {}
class   Object2   {}

Object   o1,   o2;

o1   =   new   Object1();

o2   =   o1;
o2   =   new   Object2();

這時候,o1的類型是什嗎?是Object1還是Object2?正確答案是Object1。
再舉一個例子:
class   Word   {
String   word;
public   Word(String   word){
this.word   =   word;
}
public   void   print(){
System.out.println(word);
}
}

Word   o1,   o2;

o1   =   new   Word( "Every   Day ");

o2   =   o1;
o2   =   new   Word( "Every   Night! ");

w1.print();

會出現什麼結果? "Every   Day "   還是   "Every   Night! "?仍然是 "Every   Day "。

這裡面有一個很多人特別是初學者忽視了的觀點   ――   變數可以引用對象,但變數不是對象。什麼是對象?對象初始化之後,會佔用一塊記憶體空間,嚴格意義上講,這段記憶體空間才是對象。對象建立於資料區段,而變數存在於程式碼片段;對象的入口地址是不可預知的,所以程式只能通過變數來訪問對象。

回到我們的問題上來,第一句
o1   =   new   Word( "Every   Day ");
首先建立一個Word執行個體,即對象,然後把“引用”賦值給o1。
第二句
o2   =   o1;
o1把對象的引用賦值給o2,注意賦的值是對象的引用而不是o1自身的引用。所以,在的三句
o2   =   new   Word( "Every   Night! ");
就是又建立一個新對象,再把新對象的引用賦值給o2。

因為o1和   o2之間是值傳,所以,對o2的改變絲毫不會影響到o1。

也有一種情況好像是影響到了o1,我們繼續上面的例子,給Word增加一個方法
class   Word   {
String   word;
public   Word(String   word){
this.word   =   word;
}
public   void   print(){
System.out.println(word);
}
public   void   setWord(String   word){
this.word   =   word;
}
}

Word   o1,   o2;

o1   =   new   Word( "Every   Day ");
o2   =   o1;
o2.set   Word( "Every   Night! ");

o1.print();

這時的結果是 "Every   Night! "。

那麼,這是改變了o1嗎?從嚴格意義上講,不是。因為o1隻是儲存對象的引用,執行之後,o1還是持有該對象的引用。所以,o1沒變,變的是o1所引用的對象。
3.3   final變數能改變嗎?
好了,我再出道題目:

final   Word   o3   =   new   Word( "Every   Day! ");
o3.setWord( "Every   Night! ");

能通過編譯嗎?對於final的定義大家都知道,o3是相當於一個常量,既然是常量,怎麼能再改變呢?
答案是肯定的,能。道理我想大家也明白,這裡不羅嗦了。
3.4   封裝類的賦值與傳遞
以前看過文章說,對於java基本類型及其封裝類採用值傳遞,對於對象採用引用傳遞。從langspec看,首先封裝類不是PrimitiveType,那就只能是ReferenceType,而ReferenceType的變數儲存的是引用。既然儲存的是引用,也就無從傳遞數值。那麼,這兩個觀點矛盾嗎?

首先,肯定是langspec正確。
其次,雖然前一觀點在原理上有錯誤,但卻不影響正常使用。

為什麼會出現這種情況?這是因為這些封裝類具有一個簡單類型的特徵,即,不可改變。以String為例,看一下API   Specification,不會找到能夠改變String對象的方法。任何輸出上的改變都是重建新的String對象,而不是在原對象基礎上改變。改變的是變數的內容,即,不同對象的引用。

 

 

------------------------------------------------------------------------

(::一騎絕塵::) :

 

Java中總是使用傳值調用。
在Java中,方法可以改變對象參數的狀態,卻無法改變這個對象引用(Object   reference)本
身.也就是當一個對象的執行個體被建立的時候,like   this:   Apple   a   =   new   Apple();   a   存的就是這個對象執行個體的地址。而這個地址,也就是a的值作為參數傳到某個函數中的時候,a本身是不會改變的。
電視機和遙控器可以很形象的描敘和解釋這個問題。  
可以把遙控器看作是電視機的一個引用拷貝,只要電視機存在,也就是用遙控器對準一台電視機,按遙控器上面的各種按扭(function)可以對電視機產生各種影響,但是你換一個遙控器對電視機來說是不會產生影響的,電視機並不會因為遙控器換了而變成別的電視機。同時遙控器也可以不對準原來的電視機,轉去對準別的電視機,這對原來的電視機也是不會產生影響的。一台電視機可以有多個遙控器,並且只要某個遙控器對準了這台電視機,這個遙控器就可以通過它上面的按扭改變電視機的狀態。
下面是正面只有按值傳遞的程度代碼:
//Test.java  
class   Test   {  
private   String   name;  

public   String   getName()   {  
return   name;  
}  

public   void   setName(String   name)   {  
this.name   =   name;  
}  
}  

//TestTransferParameter.java  

public   class   TestTransferParameter{  
public   static   void   main(String[]   args)   {  
Test   a   =   new   Test();  
a.setName( "a ");  
Test   b   =   new   Test();  
b.setName( "b ");  
System.out.println( "before   swap:   "   +   "a= "   +   a.getName()   +   ";   b= "   +   b.getName());  
swap(a,b);  
System.out.println( "after   swap:   "   +   "a= "   +   a.getName()   +   ";   b= "   +   b.getName());  
}  

private   static   void   swap(Test   a,   Test   b)   {  
Test   temp;  
temp   =   a;  
a   =   b;  
b   =   temp;  
System.out.println( "swaping:   "   +   "a= "   +   a.getName()   +   ";   b= "   +   b.getName());  
}  

}  

輸出結果:  
before   swap:   a=a;   b=b  
swaping:   a=b;   b=a  
after   swap:   a=a;   b=b  

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.