Posted on 2011-07-28 13:31
無很 閱讀(259)
評論(0) 編輯 收藏
按值傳參
JAVA中的參數傳遞只存在傳值方式一種,但也有傳引用的概念。
這是java參數傳遞的核心說明。java不像C/C++那樣可以通過指針符或地址符來區分傳值還是傳引用,因為它只有一種參數傳遞的方式,那就是傳值方式。這對初學java的人來說很難理解,通過下面的例子可以看出真正的傳值在java中是如何實現的。
public static void swap(int a,int b){//交換兩個變量的值
int temp = a;
a = b;
b = temp;
System.out.println("swap:"+a+","+b);
}
......
int a = 3;
int b = 5;
swap(a,b);
System.out.println(a +" "+b);//3 5
......
程序打印結果:
swap:5,3
3 5
由程序的結果可以看到,事實上,經過交換的方法swap()之后,主程序中的變量a,b的值并沒有被改變。
分析過程如下:在調用swap()方法的時候,程序把a、b的副本送到swap()方法中的變量a、b中去,而主程序中的變量a、b中的內容并沒有被改變。所以就會出現上面的結果。
其實,不僅是簡單的數據類型如此 ,如果傳遞的參數是一個對象,也會出現這樣的結果,這與簡單數據類型的情況類似,都可以劃為按值傳參一類中。我們看下面的程序及其分析過程:
public class User{
static void swap(User user1,User user2){
User user = user1;
user1 = user2;
user2 = user;
}
public static void main(String[]args){
User user3 = new User("ding", 20);
User user4 = new User("zhao", 18);
swap(user3, user4);
/*進行函數swap(user1,user2)的調用,但是并不會真正把user3和user4所指向的內存空間
*進行交換,只是在調用的時候,會把user3中存放的內存地址復制一份傳給user1,把user4中存放的
*內存地址復制一份傳給user2,相當于只是給user1和user2了一個副本,而真正的對象
*user3和user4并沒有在swap()函數中被觸及。所以函數處理的結果是使user1和user2所指向的內存空間
*進行交換,而user3和user4中的存放內容仍是原來的內容。*/
System.out.println(user3.username + " " +user3.age);//ding 20
System.out.println(user4.username + " " +user4.age);//zhao 18
}
程序的運行結果:
ding 20
zhao 18
傳遞引用
既然說java中的參數傳遞只有by value一種,為什么還要說傳遞引用呢?實際上,java與C++一樣,同樣存在對一個對象的引用進行傳遞的問題,但java中的引用傳遞機制是,把原來變量中保存的內存地址傳遞作為一個參數進行傳遞,而不是直接把引用傳過去。所以在java中仍把它稱做按值傳參。
同樣采用例子的方式來解釋java中的引用傳遞的問題:
.......
public static void changAge(User user){
User temp = user;//這時,temp中存放了和原對象相同的內存地址
temp.age = temp.age + 20;//對user的年齡進行增加的操作,
/*是對temp所指向的內存空間中的值直接進行操作,所以會對原對象的值造成影響。就相當于是傳遞了原對象的引用*/
}
......
User user = new User("li",25);//仍采用上例中的User
changAge(user);//改變user的年齡
System.out.println(user.age);//45
.......
有了上面例子的說明,我們可能就會想到,如果我們的程序中私有變量是一個對象類型的變量時,在主程序中有了這個私有變量的拷貝,是不是就有可能在修改這個拷貝時不小心把原來的私有變量的值也給改變了呢?
我們來看下面的例子:
class Test{
private Date date=new Date();
public Date getDate(){
return date;
}
public static void main(String[] args){
Test tt=new Test();
Date myDate=tt.getDate();//返回了一個私有變量的拷貝
System.out.println(myDate);
myDate.setTime(new Date().getTime()-(long)(10*365.25*24*3600*1000));//對新產生的拷貝進行修改
System.out.println(tt.getDate());
System.out.println(myDate);
}
}
先來猜一下運行的結果,是前兩句輸出一樣呢還是后兩句輸出一樣(最后一句輸出比第一句輸出的日期早十年)?很多人都會說是前兩句輸出一樣.實際上,你會驚奇地發現,輸出結果顯示后兩者輸出一樣,私有變量在程序外部被改變了,程序的封裝性遭到了破壞。
出錯的原因很微妙.因為myDate和tt指向了同一個對象,對myDate的引用更改方法自動地改變了這個類的私有方法狀態。經過測試可以知道,如果myDate被重新賦值(比如myDate=new Date()),就不會出現上面的結果。但是現在的這個程序,私有變量還是在程序外部被改變了。
如果需要返回一個指向可變對象的引用,我們就需要克隆它,這樣就不會導致上面的私有變量被更改。
上面程序就應更改為:return (Date)date.clone();就可以防止私有變量被修改的麻煩了。
再來執行上面的程序,就會出現不一樣的結果。