欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解java中的拷貝機制

 更新時間:2017年02月15日 10:38:24   作者:QuinnNorris  
這篇文章主要給大家深入介紹了java中的拷貝機制,網(wǎng)上關(guān)于java中拷貝的文章也很多,但覺得有必要再深的介紹下java的拷貝機制,有需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。

前言

眾所周知在Java中,拷貝分為深拷貝和淺拷貝兩種。java在公共超類Object中實現(xiàn)了一種叫做clone的方法,這種方法clone出來的新對象為淺拷貝,而通過自己定義的clone方法為深拷貝。

(一)Object中clone方法

如果我們new出一個新對象,用一個聲明去引用它,之后又用另一個聲明去引用前一個聲明,那么最后的結(jié)果是:這兩個聲明的變量將指向同一個對象,一處被改全部被改。如果我們想創(chuàng)建一個對象的copy,這個copy和對象的各種屬性完全相同,而且修改這個copy和原對象毫無關(guān)系,那么這個時候我們就要用到clone方法。

package Clone;

import java.util.Date;

/**
 * 
 * @author QuinnNorris 
 * java中的兩種拷貝機制
 */
public class Clone {

 /**
 * @param args
 * @throws CloneNotSupportedException 
 */
 public static void main(String[] args) throws CloneNotSupportedException {
 // TODO Auto-generated method stub

 ClassA valA = new ClassA(1, "old", new Date());
 // 聲明一個新的ClassA對象,我們不需要太關(guān)注ClassA的功能
 ClassA valB = valA;
 // 將valA引用的對象賦給valB
 valA.setObject("new");
 // 更改valA中的值,此時valB也被更改了,因為valA和valB指向同一個對象

 valB = valA.clone();//通過clone方法制造副本
 }
}

ClassA類中關(guān)于clone方法的重寫部分:

//需要實現(xiàn)Cloneable接口
public class ClassA implements Cloneable {

 public ClassA clone() throws CloneNotSupportedException {
 return (ClassA) super.clone();//調(diào)用父類(Object)的clone方法
 }

}

1.如何使用Object中clone方法的

有人總結(jié)使用clone方法的四條法則,我們一起分享一下:

  1. 為了獲取對象的一份拷貝,我們可以利用Object類的clone()方法。
  2. 在派生類中覆蓋基類的clone()方法,并聲明為public。
  3. 在派生類的clone()方法中,調(diào)用super.clone() 
  4. 在派生類中實現(xiàn)Cloneable接口。

2.protected修飾的clone方法

在java.lang.Object的中,他將clone方法設(shè)置為protected修飾,這是很特殊的一種情況。protected的作用域是:包可見+可繼承。之所以這樣設(shè)置,是因為這個方法要返回的是克隆出來的對象,即clone方法要去克隆的類型是未知的,沒有辦法確定返回值的類型,自然只能讓子孫后代來實現(xiàn)它重寫它,為了能夠讓后代繼承而又不過與張開,設(shè)置為了protected類型。

3.實現(xiàn)clone方法需要實現(xiàn)Cloneable接口

那么我們重寫clone方法的時候為什么要去實現(xiàn)Cloneable接口呢?事實上,Cloneable接口是java中的一個標記接口,標記接口是指那些沒有方法和屬性的接口,他們存在只是為了讓大家知道一些信息,而且在用:xxx instanceof Cloneable 的時候可以進行判斷。Cloneable這個接口的出現(xiàn)就是為了讓設(shè)計者知道要進行克隆處理了。如果一個對象需要克隆,但是沒有實現(xiàn)(實際上,這里的“實現(xiàn)”換成“寫上”更準確)Cloneable接口,那么會產(chǎn)生一個已檢驗異常。

4.實現(xiàn)clone方法需要調(diào)用父類的clone

我們?yōu)榱诉_到復(fù)制一個和調(diào)用方法的這個對象一模一樣的對象的目的,我們需要使用父類的clone方法,父類也以此類推,知道達到了Object的clone方法,那么Object的clone方法有什么用呢?API中是這樣說的:

protected Object clone( ) throws CloneNotSupportedException

創(chuàng)建并返回此對象的一個副本。

“副本”的準確含義可能依賴于對象的類。這樣做的目的是,對于任何對象 x,

表達式: x.clone() != x為 true,

表達式: x.clone().getClass() == x.getClass()也為 true

但這些并非必須要滿足的要求。

一般情況下:

x.clone().equals(x)為 true,但這并非必須要滿足的要求。

按照慣例,返回的對象應(yīng)該通過調(diào)用 super.clone 獲得。

如果一個類及其所有的超類(Object 除外)都遵守此約定,則 x.clone().getClass() == x.getClass()

上面就是API中對clone的一部分基本講解。我們可以得出結(jié)論的是,只要合理的調(diào)用了spuer.clone( )它就會返回一個被克隆的對象。在運行時刻,Object中的clone()識別出你要復(fù)制的是哪一個對象,然后為此對象分配空間,并進行對象的復(fù)制,將原始對象的內(nèi)容一一復(fù)制到新對象的存儲空間中。在這個克隆對象中,所有的屬性都和被克隆的對象的屬性相同,而這些相同的屬性分為兩種:

第一種 : 八大原始類型和不可變的對象(比如String)

第二種 : 其他類對象

對于第一種,clone方法將他們的值設(shè)置為原對象的值,沒有任何問題。對于第二種,clone方法只是簡單的將復(fù)制的新對象的引用指向原對象指向的引用,第二種的類對象會被兩個對象修改。那么這個時候就涉及一個深淺拷貝的概念了。

(二)淺拷貝

淺拷貝:被拷貝對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺復(fù)制僅僅復(fù)制所考慮的對象,而不復(fù)制它所引用的對象。

比如舉個例子,一個類A中有另外一個類B類型的變量。在A重寫clone函數(shù)調(diào)用super.clone的時候,創(chuàng)建的新對象和原來對象中的類B類型的變量是同一個,他們指向了同一個B的類型變量。如果在A中對B的變量做了修改,在新的拷貝出來的對象中B的變量也會被同樣的修改。

請記住,直接調(diào)用super.clone實現(xiàn)的clone方法全部都是淺拷貝。

(三)深拷貝

深拷貝:被拷貝對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量將指向被復(fù)制過的新對象,而不再是原有的那些被引用的對象。換言之,深復(fù)制把要復(fù)制的對象所引用的對象都復(fù)制了一遍。

通俗的說,如果說淺拷貝,開始的時候是兩條線,如果在最后有一個其他類的變量,那么這兩條線最后會合二為一,共同指向這變量,都能對他進行操作。深拷貝則是完完全全的兩條線,互不干涉,因為他已經(jīng)把所有的內(nèi)部中的變量的對象全都復(fù)制一遍了。

深拷貝在代碼中,需要在clone方法中多書寫調(diào)用這個類中其他類的變量的clone函數(shù)。

(四)串行化深拷貝

在框架中,有的時候我們發(fā)現(xiàn)其中并沒有重寫clone方法,那么我們在需要拷貝一個對象的時候是如何去操作的呢?答案是我們經(jīng)常會使用串行化方法,實現(xiàn)Serializable接口。

去尋找其他的方法來替代深拷貝也是無可奈何的事情,如果采用傳統(tǒng)的深拷貝,難道你拷貝一個對象的時候向其中追無數(shù)層來拷貝完所有的對象變量么?先不談這么做的時間消耗,僅僅是寫這樣的代碼都會讓人望而生畏。串行化深拷貝就是這樣一個相對簡單的方法。

把對象寫到流里的過程是串行化(Serilization)過程,但是在Java程序師圈子里又非常形象地稱為“冷凍”或者“腌咸菜(picking)”過程;而把對象從流中讀出來的并行化(Deserialization)過程則叫做 “解凍”或者“回鮮(depicking)”過程。應(yīng)當(dāng)指出的是,寫在流里的是對象的一個拷貝,而原對象仍然存在于JVM里面,因此“腌成咸菜”的只是對象的一個拷貝,Java咸菜還可以回鮮。

上面是網(wǎng)上的專業(yè)解釋,我也不在這里班門弄斧了。在Java語言里深復(fù)制一個對象,常??梢韵仁箤ο髮崿F(xiàn)Serializable接口,然后把對象(實際上只是對象的一個拷貝)寫到一個流里(腌成咸菜),再從流里讀出來(把咸菜回鮮),便可以重建對象。

public Object deepClone() 
{ 
 //寫入對象 
 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); 
 ObjectOutputStream oo=new ObjectOutputStream(bo); 
 oo.writeObject(this); 
 //讀取對象
 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); 
 ObjectInputStream oi=new ObjectInputStream(bi); 
 return(oi.readObject()); 
}

雖然這種學(xué)院派的代碼看起來很復(fù)雜,其實只是把對象放到流里,再拿出來。相比較分析判斷無數(shù)的clone,這樣簡直是再簡單不過了。這樣做的前提是對象以及對象內(nèi)部所有引用到的對象都是可串行化的,否則,就需要仔細考察那些不可串行化的對象是否設(shè)成transient。

transient:一個對象只要實現(xiàn)了Serilizable接口,這個對象就可以被序列化(序列化是指將java代碼以字節(jié)序列的形式寫出,即我們上面代碼前三行寫入對象),Java的這種序列化模式為開發(fā)者提供了很多便利,可以不必關(guān)系具體序列化的過程,只要這個類實現(xiàn)了Serilizable接口,這個的所有屬性和方法都會自動序列化。但是有種情況是有些屬性是不需要序列號的,所以就用到這個關(guān)鍵字。只需要實現(xiàn)Serilizable接口,將不需要序列化的屬性前添加關(guān)鍵字transient,序列化對象的時候,這個屬性就不會序列化到指定的目的地中。

總結(jié)

在實際的應(yīng)用中,深拷貝和淺拷貝只是兩個概念,不一定誰比誰好,要按照實際的工作來確定如何去拷貝一個對象。如果在數(shù)據(jù)庫操作方面,為了取出一張表時不涉及其他的表,肯定需要使用淺拷貝,而在框架的Serializable中,雖然耗時,但是深拷貝是非常有必要的。

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

  • Java虛擬機JVM類加載機制(從類文件到虛擬機)

    Java虛擬機JVM類加載機制(從類文件到虛擬機)

    所謂的類加載機制就是虛擬機將class文件加載到內(nèi)存,并對數(shù)據(jù)進行驗證,轉(zhuǎn)換解析和初始化,形成虛擬機可以直接使用的java類型,本文給大家介紹類加載機制過程從類文件到虛擬機的詳細說明,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • MybatisPlus3.5.5與pagehelper?starter2.1.0沖突的問題解決

    MybatisPlus3.5.5與pagehelper?starter2.1.0沖突的問題解決

    在使用MybatisPlus?3.5.5與PageHelper?Starter?2.1.0時,由于引用了不同版本的jsqlparser庫(4.6與4.7),會導(dǎo)致運行時錯誤,解決方案涉及確認依賴版本,本文就來介紹一下,感興趣的同學(xué)可以下載學(xué)習(xí)
    2024-10-10
  • Mybatis-plus與Mybatis依賴沖突問題解決方法

    Mybatis-plus與Mybatis依賴沖突問題解決方法

    ,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧這篇文章主要介紹了Mybatis-plus與Mybatis依賴沖突問題解決方法
    2021-04-04
  • SpringBoot?實現(xiàn)微信推送模板的示例代碼

    SpringBoot?實現(xiàn)微信推送模板的示例代碼

    這篇文章主要介紹了SpringBoot?實現(xiàn)微信推送模板,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-12-12
  • Spring?this調(diào)用當(dāng)前類方法無法攔截的示例代碼

    Spring?this調(diào)用當(dāng)前類方法無法攔截的示例代碼

    這篇文章主要介紹了Spring?this調(diào)用當(dāng)前類方法無法攔截,通過debug 查看這個proxyService1 和this的區(qū)別,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • SpringBoot+Elasticsearch實現(xiàn)數(shù)據(jù)搜索的方法詳解

    SpringBoot+Elasticsearch實現(xiàn)數(shù)據(jù)搜索的方法詳解

    Elasticsearch是一個基于Lucene的搜索服務(wù)器。它提供了一個分布式多用戶能力的全文搜索引擎,基于RESTful?web接口。本文將利用SpringBoot整合Elasticsearch實現(xiàn)海量級數(shù)據(jù)搜索,需要的可以參考一下
    2022-05-05
  • java應(yīng)用cpu飆升(超過100%)故障排查步驟

    java應(yīng)用cpu飆升(超過100%)故障排查步驟

    在Java并發(fā)編程計算密集型要進行大量的計算、邏輯判斷等操作,消耗CPU資源,比如計算圓周率、對視頻進行高清解碼等等,下面這篇文章主要給大家介紹了關(guān)于java應(yīng)用cpu飆升(超過100%)故障排查步驟的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • SpringCloud?Gateway實現(xiàn)API接口加解密

    SpringCloud?Gateway實現(xiàn)API接口加解密

    這篇文章主要為大家介紹了SpringCloud?Gateway如何實現(xiàn)API接口加解密的,文中的示例代碼講解詳細,對我們學(xué)習(xí)有一定的幫助,需要的可以參考一下
    2022-06-06
  • Java簡單計算兩個日期月數(shù)差的方法

    Java簡單計算兩個日期月數(shù)差的方法

    這篇文章主要介紹了Java簡單計算兩個日期月數(shù)差的方法,結(jié)合實例形式分析了java使用Calendar類進行日期時間操作相關(guān)技巧,需要的朋友可以參考下
    2017-06-06
  • Java實現(xiàn)一個簡單的長輪詢的示例代碼

    Java實現(xiàn)一個簡單的長輪詢的示例代碼

    長輪詢是與服務(wù)器保持即時通信的最簡單的方式,它不使用任何特定的協(xié)議,例如 WebSocket ,所以也不依賴于瀏覽器版本等外部條件的兼容性。本文將用Java實現(xiàn)一個簡單的長輪詢,需要的可以參考一下
    2022-08-08

最新評論