• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            chenglong7997

            java中clone()

             目錄 

            預(yù)備知識(shí) 
            為什么要clone 
            Object的clone以及為什么如此實(shí)現(xiàn) 
            如何clone 
            對(duì)clone的態(tài)度 
            其他的選擇 
            和Serializable的比較 
            性能 

            預(yù)備知識(shí) 

            為了理解java的clone,有必要先溫習(xí)以下的知識(shí)。 
            java的類(lèi)型,java的類(lèi)型分為兩大類(lèi),一類(lèi)為primitive,如int,另一類(lèi)為引用類(lèi)型,如String,Object等等。 
            java引用類(lèi)型的存儲(chǔ),java的引用類(lèi)型都是存儲(chǔ)在堆上的
            public class B { int a; String b; public B(int a, String b) { super(); this.a = a; this.b = b; } }

            對(duì)這樣一個(gè)引用類(lèi)型的實(shí)例,我們可以推測(cè),在堆上它的內(nèi)存存儲(chǔ)形式(除去指向class的引用,鎖的管理等等內(nèi)務(wù)事務(wù)所占內(nèi)存),應(yīng)該有一個(gè)int值表示a,以及一個(gè)引用,該引用指向b在堆上的存儲(chǔ)空間。 
             

            為什么要clone 

            恩,因?yàn)樾枰U話(huà)。 
            有名的GoF設(shè)計(jì)模式里有一個(gè)模式為原型模式,用原型實(shí)例指定創(chuàng)建對(duì)象的種類(lèi),并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象. 
            簡(jiǎn)單的說(shuō)就是clone一個(gè)對(duì)象實(shí)例。使得clone出來(lái)的copy和原有的對(duì)象一模一樣。 

            插一個(gè)簡(jiǎn)單使用clone的例子,如果一個(gè)對(duì)象內(nèi)部有可變對(duì)象實(shí)例的話(huà),public API不應(yīng)該直接返回該對(duì)象的引用,以防調(diào)用方的code改變?cè)搶?duì)象的內(nèi)部狀態(tài)。這個(gè)時(shí)候可以返回該對(duì)象的clone。 

            問(wèn)題來(lái)了,什么叫一模一樣。 
            一般來(lái)說(shuō),有 
            x.clone() != x 
            x.clone().getClass() == x.getClass() 
            x.clone().equals(x) 
            但是這些都不是強(qiáng)制的。 
            我們需要什么樣的clone就搞出什么樣的clone好了。 
            一般而言,我們要的clone應(yīng)該是這樣的。copy和原型的內(nèi)容一樣,但是又是彼此隔離的。即在clone之后,改變其中一個(gè)不影響另外一個(gè)。 


            Object的clone以及為什么如此實(shí)現(xiàn) 

            Object的clone的行為是最簡(jiǎn)單的。以堆上的內(nèi)存存儲(chǔ)解釋的話(huà)(不計(jì)內(nèi)務(wù)內(nèi)存),對(duì)一個(gè)對(duì)象a的clone就是在堆上分配一個(gè)和a在堆上所占存儲(chǔ)空間一樣大的一塊地方,然后把a(bǔ)的堆上內(nèi)存的內(nèi)容復(fù)制到這個(gè)新分配的內(nèi)存空間上。 
            看例子。 
            class User { 	String name; 	int age; } class Account implements Cloneable { 	User user; 	long balance; 	@Override 	public Object clone() throws CloneNotSupportedException { 		return super.clone(); 	} }


            		// user. 		User user = new User(); 		user.name = "user"; 		user.age = 20; 		// account. 		Account account = new Account(); 		account.user = user; 		account.balance = 10000; 		// copy. 		Account copy = (Account) account.clone(); 		// balance因?yàn)槭莗rimitive,所以copy和原型是相等且獨(dú)立的。 		Assert.assertEquals(copy.balance, account.balance); 		copy.balance = 20000; 		// 改變copy不影響原型。 		Assert.assertTrue(copy.balance != account.balance); 		// user因?yàn)槭且妙?lèi)型,所以copy和原型的引用是同一的。 		Assert.assertTrue(copy.user == account.user); 		copy.user.name = "newName"; 		// 改變的是同一個(gè)東西。 		Assert.assertEquals("newName", account.user.name);

             

            恩,默認(rèn)實(shí)現(xiàn)是幫了我們一些忙,但是不是全部。 
            primitive的確做到了相等且隔離。 
            引用類(lèi)型僅僅是復(fù)制了一下引用,copy和原型引用的東西是一樣的。 
            這個(gè)就是所謂的淺copy了。 
            要實(shí)現(xiàn)深copy,即復(fù)制原型中對(duì)象的內(nèi)存copy,而不僅僅是一個(gè)引用。只有自己動(dòng)手了。 
            等等,是不是所有的引用類(lèi)型都需要深copy呢? 
            不是! 
            我們之所以要深copy,是因?yàn)槟J(rèn)的實(shí)現(xiàn)提供的淺copy不是隔離的,換言之,改變copy的東西,會(huì)影響到原型的內(nèi)部。比如例子中,改變copy的user的name,影響了原型。 
            如果我們要copy的類(lèi)是不可變的呢,如String,沒(méi)有方法可以改變它的內(nèi)部狀態(tài)呢。 
            class User implements Cloneable { 	String name; 	int age; 	@Override 	public Object clone() throws CloneNotSupportedException { 		return super.clone(); 	} }

            		// user. 		User user = new User(); 		user.name = "user"; 		user.age = 20; 		// copy 		User copy = (User) user.clone(); 		// age因?yàn)槭莗rimitive,所以copy和原型是相等且獨(dú)立的。 		Assert.assertEquals(copy.age, user.age); 		copy.age = 30; 		// 改變copy不影響原型。 		Assert.assertTrue(copy.age != user.age); 		// name因?yàn)槭且妙?lèi)型,所以copy和原型的引用是同一的。 		Assert.assertTrue(copy.name == user.name); 		// String為不可變類(lèi)。沒(méi)有辦法可以通過(guò)對(duì)copy.name的字符串的操作改變這個(gè)字符串。 		// 改變引用新的對(duì)象不會(huì)影響原型。 		copy.name = "newname"; 		Assert.assertEquals("newname", copy.name); 		Assert.assertEquals("user", user.name);
            可見(jiàn),在考慮clone時(shí),primitive和不可變對(duì)象類(lèi)型是可以同等對(duì)待的。 

            java為什么如此實(shí)現(xiàn)clone呢? 
            也許有以下考慮。 
            1 效率和簡(jiǎn)單性,簡(jiǎn)單的copy一個(gè)對(duì)象在堆上的的內(nèi)存比遍歷一個(gè)對(duì)象網(wǎng)然后內(nèi)存深copy明顯效率高并且簡(jiǎn)單。 
            2 不給別的類(lèi)強(qiáng)加意義。如果A實(shí)現(xiàn)了Cloneable,同時(shí)有一個(gè)引用指向B,如果直接復(fù)制內(nèi)存進(jìn)行深copy的話(huà),意味著B(niǎo)在意義上也是支持Clone的,但是這個(gè)是在使用B的A中做的,B甚至都不知道。破壞了B原有的接口。 
            3 有可能破壞語(yǔ)義。如果A實(shí)現(xiàn)了Cloneable,同時(shí)有一個(gè)引用指向B,該B實(shí)現(xiàn)為單例模式,如果直接復(fù)制內(nèi)存進(jìn)行深copy的話(huà),破壞了B的單例模式。 
            4 方便且更靈活,如果A引用一個(gè)不可變對(duì)象,則內(nèi)存deep copy是一種浪費(fèi)。Shadow copy給了程序員更好的靈活性。 

            如何clone 
            clone三部曲。 
            1 聲明實(shí)現(xiàn)Cloneable接口。 
            2 調(diào)用super.clone拿到一個(gè)對(duì)象,如果父類(lèi)的clone實(shí)現(xiàn)沒(méi)有問(wèn)題的話(huà),在該對(duì)象的內(nèi)存存儲(chǔ)中,所有父類(lèi)定義的field都已經(jīng)clone好了,該類(lèi)中的primitive和不可變類(lèi)型引用也克隆好了,可變類(lèi)型引用都是淺copy。 
            3 把淺copy的引用指向原型對(duì)象新的克隆體。 
            給個(gè)例子。 
            class User implements Cloneable { 	String name; 	int age; 	@Override 	public User clone() throws CloneNotSupportedException { 		return (User) super.clone(); 	} } class Account implements Cloneable { 	User user; 	long balance; 	@Override 	public Account clone() throws CloneNotSupportedException { 		Account account = null; 		account = (Account) super.clone(); 		if (user != null) { 			account.user = user.clone(); 		} 		return account; 	} }


             

            對(duì)clone的態(tài)度 

            clone嘛,我覺(jué)得是個(gè)好東西,畢竟系統(tǒng)默認(rèn)實(shí)現(xiàn)已經(jīng)幫我們做了很多事情了。 
            但是它也是有缺點(diǎn)的。 
            1 手工維護(hù)clone的調(diào)用鏈。這個(gè)問(wèn)題不大,程序員有責(zé)任做好。 
            2 如果class的field是個(gè)final的可變類(lèi),就不行了。三部曲的第三步?jīng)]有辦法做了。 

            考慮一個(gè)類(lèi)對(duì)clone的態(tài)度,有如下幾種。 
            1 公開(kāi)支持:好吧,按照clone三部曲實(shí)現(xiàn)吧。前提是父類(lèi)支持(公開(kāi)或者默默)。 
            2 默默支持:不實(shí)現(xiàn)Cloneable接口,但是在類(lèi)里面有正確的protected的clone實(shí)現(xiàn),這樣,該類(lèi)不支持clone,但是它的子類(lèi)如果想支持的話(huà)也不妨礙。 
            3 不支持:好吧,為了明確該目的,提供一個(gè)拋CloneNotSupportedException 異常的protected的clone實(shí)現(xiàn)。 
            4 看情況支持:該類(lèi)內(nèi)部可以保存其他類(lèi)的實(shí)例,如果其他類(lèi)支持則該類(lèi)支持,如果其他類(lèi)不支持,該類(lèi)沒(méi)有辦法,只有不支持。 


            其他的選擇 

            可以用原型構(gòu)造函數(shù),或者靜態(tài)copy方法來(lái)手工制作一個(gè)對(duì)象的copy。 
            好處是即使class的field為final,也不會(huì)影響該方法的使用。不好的地方是所有的primitive賦值都得自己維護(hù)。 


            和Serializable的比較 

            使用Serializable同樣可以做到對(duì)象的clone。但是: 
            Cloneable本身就是為clone設(shè)計(jì)的,雖然有一些缺點(diǎn),但是如果它可以clone的話(huà)無(wú)疑用它來(lái)做clone比較合適。如果不行的話(huà)用原型構(gòu)造函數(shù),或者靜態(tài)copy方法也可以。 

            Serializable制作clone的話(huà),添加了太多其它的東西,增加了復(fù)雜性。 
            1 所有的相關(guān)的類(lèi)都得支持Serializable。這個(gè)相比支持Cloneable只會(huì)工作量更大 
            2 Serializable添加了更多的意義,除了提供一個(gè)方法用Serializable制作Clone,該類(lèi)等于也添加了其它的public API,如果一個(gè)類(lèi)實(shí)現(xiàn)了Serializable,等于它的2進(jìn)制形式就已經(jīng)是其API的一部分了,不便于該類(lèi)以后內(nèi)部的改動(dòng)。 
            3 當(dāng)類(lèi)用Serializable來(lái)實(shí)現(xiàn)clone時(shí),用戶(hù)如果保存了一個(gè)老版本的對(duì)象2進(jìn)制,該類(lèi)升級(jí),用戶(hù)用新版本的類(lèi)反系列化該對(duì)象,再調(diào)用該對(duì)象用Serializable實(shí)現(xiàn)的clone。這里為了一個(gè)clone的方法又引入了類(lèi)版本兼容性的問(wèn)題。不劃算。 


            性能 

            不可否認(rèn),JVM越來(lái)越快了。 
            但是系統(tǒng)默認(rèn)的native實(shí)現(xiàn)還是挺快的。 
            clone一個(gè)有100個(gè)元素的int數(shù)組,用系統(tǒng)默認(rèn)的clone比靜態(tài)copy方法快2倍左右。 

            posted on 2012-04-11 14:08 Snape 閱讀(261) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Java

            導(dǎo)航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計(jì)

            常用鏈接

            留言簿

            隨筆分類(lèi)

            隨筆檔案

            文章分類(lèi)

            文章檔案

            my

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            亚洲综合久久综合激情久久| avtt天堂网久久精品| 久久精品国产亚洲一区二区| 99久久免费国产精品特黄| 国内精品久久久久影院网站| 99久久超碰中文字幕伊人| 久久精品国产乱子伦| 久久只这里是精品66| 亚洲国产成人精品91久久久| 精品久久久久久99人妻| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 国产精品久久久久久福利69堂| 亚洲熟妇无码另类久久久| 午夜天堂精品久久久久| 亚洲国产另类久久久精品| 国产成人精品综合久久久久| 中文字幕乱码久久午夜| 亚洲精品乱码久久久久久按摩 | 国产精品久久永久免费| 996久久国产精品线观看| 狠狠色丁香婷综合久久| 94久久国产乱子伦精品免费| 久久亚洲国产欧洲精品一| 狠狠色综合久久久久尤物| 久久天天躁狠狠躁夜夜2020老熟妇 | 2021少妇久久久久久久久久| 99久久久精品免费观看国产| 国产精品福利一区二区久久| 99久久久久| 久久笫一福利免费导航| 久久精品水蜜桃av综合天堂| 久久免费国产精品一区二区| 99久久www免费人成精品| 久久免费99精品国产自在现线 | 久久99精品综合国产首页| 久久美女人爽女人爽| 婷婷久久综合九色综合绿巨人| 久久天天躁狠狠躁夜夜躁2014| 久久精品麻豆日日躁夜夜躁| 国内精品久久久久久中文字幕| 99久久免费国产精品特黄|