|
Java語言的一個(gè)優(yōu)點(diǎn)就是取消了指針的概念,但也導(dǎo)致了許多程序員在編程中常常忽略了對象與引用的區(qū)別,本文會試圖澄清這一概念。并且由于Java不能通過簡單的賦值來解決對象復(fù)制的問題,在開發(fā)過程中,也常常要要應(yīng)用clone()方法來復(fù)制對象。本文會讓你了解什么是影子clone與深度clone,認(rèn)識它們的區(qū)別、優(yōu)點(diǎn)及缺點(diǎn)。
看到這個(gè)標(biāo)題,是不是有點(diǎn)困惑:Java語言明確說明取消了指針,因?yàn)橹羔樛窃趲矸奖愕耐瑫r(shí)也是導(dǎo)致代碼不安全的根源,同時(shí)也會使程序的變得非常復(fù)雜難以理解,濫用指針寫成的代碼不亞于使用早已臭名昭著的"GOTO"語句。Java放棄指針的概念絕對是極其明智的。但這只是在Java語言中沒有明確的指針定義,實(shí)質(zhì)上每一個(gè)new語句返回的都是一個(gè)指針的引用,只不過在大多時(shí)候Java中不用關(guān)心如何操作這個(gè)"指針",更不用象在操作C++的指針那樣膽戰(zhàn)心驚。唯一要多多關(guān)心的是在給函數(shù)傳遞對象的時(shí)候。如下例程:
package reference; class Obj{ String str = "init value"; public String toString(){ return str; } } public class ObjRef{ Obj aObj = new Obj(); int aInt = 11; public void changeObj(Obj inObj){ inObj.str = "changed value"; } public void changePri(int inInt){ inInt = 22; } public static void main(String[] args) { ObjRef oRef = new ObjRef();
System.out.println("Before call changeObj() method: " + oRef.aObj); oRef.changeObj(oRef.aObj); System.out.println("After call changeObj() method: " + oRef.aObj);
System.out.println("==================Print Primtive================="); System.out.println("Before call changePri() method: " + oRef.aInt); oRef.changePri(oRef.aInt); System.out.println("After call changePri() method: " + oRef.aInt); } }
/* RUN RESULT Before call changeObj() method: init value After call changeObj() method: changed value ==================Print Primtive================= Before call changePri() method: 11 After call changePri() method: 11
* */
這段代碼的主要部分調(diào)用了兩個(gè)很相近的方法,changeObj()和changePri()。唯一不同的是它們一個(gè)把對象作為輸入?yún)?shù),另一個(gè)把Java中的基本類型int作為輸入?yún)?shù)。并且在這兩個(gè)函數(shù)體內(nèi)部都對輸入的參數(shù)進(jìn)行了改動??此埔粯拥姆椒?,程序輸出的結(jié)果卻不太一樣。changeObj()方法真正的把輸入的參數(shù)改變了,而changePri()方法對輸入的參數(shù)沒有任何的改變。 從這個(gè)例子知道Java對對象和基本的數(shù)據(jù)類型的處理是不一樣的。和C語言一樣,當(dāng)把Java的基本數(shù)據(jù)類型(如int,char,double等)作為入口參數(shù)傳給函數(shù)體的時(shí)候,傳入的參數(shù)在函數(shù)體內(nèi)部變成了局部變量,這個(gè)局部變量是輸入?yún)?shù)的一個(gè)拷貝,所有的函數(shù)體內(nèi)部的操作都是針對這個(gè)拷貝的操作,函數(shù)執(zhí)行結(jié)束后,這個(gè)局部變量也就完成了它的使命,它影響不到作為輸入?yún)?shù)的變量。這種方式的參數(shù)傳遞被稱為"值傳遞"。而在Java中用對象的作為入口參數(shù)的傳遞則缺省為"引用傳遞",也就是說僅僅傳遞了對象的一個(gè)"引用",這個(gè)"引用"的概念同C語言中的指針引用是一樣的。當(dāng)函數(shù)體內(nèi)部對輸入變量改變時(shí),實(shí)質(zhì)上就是在對這個(gè)對象的直接操作。
除了在函數(shù)傳值的時(shí)候是"引用傳遞",在任何用"="向?qū)ο笞兞抠x值的時(shí)候都是"引用傳遞"。如:
package reference; class PassObj { String str = "init value"; } public class ObjPassvalue { public static void main(String[] args) { PassObj objA = new PassObj(); PassObj objB = objA;
objA.str = "changed in objA"; System.out.println("Print objB.str value: " + objB.str); } } /* RUN RESULT Print objB.str value: changed in objA */
第一句是在內(nèi)存中生成一個(gè)新的PassObj對象,然后把這個(gè)PassObj的引用賦給變量objA,第二句是把PassObj對象的引用又賦給了變量objB。此時(shí)objA和objB是兩個(gè)完全一致的變量,以后任何對objA的改變都等同于對objB的改變。
即使明白了Java語言中的"指針"概念也許還會不經(jīng)意間犯下面的錯(cuò)誤。
|