一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語言 - JAVA教程 - Java中的深拷貝和淺拷貝介紹

Java中的深拷貝和淺拷貝介紹

2019-11-15 14:29java教程網(wǎng) JAVA教程

對象拷貝(Object Copy)就是將一個對象的屬性拷貝到另一個有著相同類類型的對象中去。在程序中拷貝對象是很常見的,主要是為了在新的上下文環(huán)境中復(fù)用對象的部分或全部 數(shù)據(jù)。Java中有三種類型的對象拷貝:淺拷貝(Shallow Copy)、深

一、引言
   對象拷貝(Object Copy)就是將一個對象的屬性拷貝到另一個有著相同類類型的對象中去。在程序中拷貝對象是很常見的,主要是為了在新的上下文環(huán)境中復(fù)用對象的部分或全部 數(shù)據(jù)。Java中有三種類型的對象拷貝:淺拷貝(Shallow Copy)、深拷貝(Deep Copy)、延遲拷貝(Lazy Copy)。

二、淺拷貝

1、什么是淺拷貝
   淺拷貝是按位拷貝對象,它會創(chuàng)建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址 ,因此如果其中一個對象改變了這個地址,就會影響到另一個對象。

 

Java中的深拷貝和淺拷貝介紹

  在圖中,SourceObject有一個int類型的屬性 "field1"和一個引用類型屬性"refObj"(引用ContainedObject類型的對象)。當(dāng)對SourceObject做淺拷貝時,創(chuàng)建了CopiedObject,它有一個包含"field1"拷貝值的屬性"field2"以及仍指向refObj本身的引用。由于"field1"是基本類型,所以只是將它的值拷貝給"field2",但是由于"refObj"是一個引用類型, 所以CopiedObject指向"refObj"相同的地址。因此對SourceObject中的"refObj"所做的任何改變都會影響到CopiedObject。

2、如何實現(xiàn)淺拷貝

下面是實現(xiàn)淺拷貝的一個例子 
 

復(fù)制代碼代碼如下:

public class Subject {
    private String name; 
   public Subject(String s) { 
      name = s; 
   } 
   public String getName() { 
      return name; 
   } 
   public void setName(String s) { 
      name = s; 
   } 
}
public class Student implements Cloneable { 
    // 對象引用 
   private Subject subj; 

   private String name; 

   public Student(String s, String sub) { 
      name = s; 
      subj = new Subject(sub); 
   } 

   public Subject getSubj() { 
      return subj; 
   } 

   public String getName() { 
      return name; 
   } 

   public void setName(String s) { 
      name = s; 
   } 

   /** 
    *  重寫clone()方法 
    * @return 
    */ 
   public Object clone() { 
      //淺拷貝 
      try { 
         // 直接調(diào)用父類的clone()方法
         return super.clone(); 
      } catch (CloneNotSupportedException e) { 
         return null; 
      } 
   } 
}
public class CopyTest {
    public static void main(String[] args) {
        // 原始對象
        Student stud = new Student("John", "Algebra");
        System.out.println("Original Object: " + stud.getName() + " - " + stud.getSubj().getName());
        // 拷貝對象
        Student clonedStud = (Student) stud.clone();
        System.out.println("Cloned Object: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
        // 原始對象和拷貝對象是否一樣:
        System.out.println("Is Original Object the same with Cloned Object: " + (stud == clonedStud));
        // 原始對象和拷貝對象的name屬性是否一樣
        System.out.println("Is Original Object's field name the same with Cloned Object: " +
     (stud.getName() == clonedStud.getName()));
        // 原始對象和拷貝對象的subj屬性是否一樣
        System.out.println("Is Original Object's field subj the same with Cloned Object: " +
    (stud.getSubj() == clonedStud.getSubj()));
        stud.setName("Dan");
        stud.getSubj().setName("Physics");
        System.out.println("Original Object after it is updated: " + stud.getName() + " - " +
     stud.getSubj().getName());
        System.out.println("Cloned Object after updating original object: " + clonedStud.getName() +
     " - " + clonedStud.getSubj().getName());
    }
}

 

輸出結(jié)果如下: 
 Original Object: John - Algebra
 Cloned Object: John - Algebra
 Is Original Object the same with Cloned Object: false
 Is Original Object's field name the same with Cloned Object: true
 Is Original Object's field subj the same with Cloned Object: true
 Original Object after it is updated: Dan - Physics
 Cloned Object after updating original object: John - Physics

     在這個例子中,我讓要拷貝的類Student實現(xiàn)了Clonable接口并重寫Object類的clone()方法,然后在方法內(nèi)部調(diào)用super.clone()方法。從輸出結(jié)果中我們可以看到,對原始對象stud的"name"屬性所做的改變并沒有影響到拷貝對象clonedStud,但是對引用對象subj的"name"屬性所做的改變影響到了拷貝對象clonedStud。

三、深拷貝
1、什么是深拷貝
   深拷貝會拷貝所有的屬性,并拷貝屬性指向的動態(tài)分配的內(nèi)存。當(dāng)對象和它所引用的對象一起拷貝時即發(fā)生深拷貝。深拷貝相比于淺拷貝速度較慢并且花銷較大。

Java中的深拷貝和淺拷貝介紹

在上圖中,SourceObject有一個int類型的屬性 "field1"和一個引用類型屬性"refObj1"(引用ContainedObject類型的對象)。當(dāng)對SourceObject做深拷貝時,創(chuàng)建了CopiedObject,它有一個包含"field1"拷貝值的屬性"field2"以及包含"refObj1"拷貝值的引用類型屬性"refObj2" 。因此對SourceObject中的"refObj"所做的任何改變都不會影響到CopiedObject

2、如何實現(xiàn)深拷貝
   下面是實現(xiàn)深拷貝的一個例子。只是在淺拷貝的例子上做了一點(diǎn)小改動,Subject 和CopyTest 類都沒有變化。
 

復(fù)制代碼代碼如下:

public class Student implements Cloneable { 
   // 對象引用 
   private Subject subj; 

   private String name; 

   public Student(String s, String sub) { 
      name = s; 
      subj = new Subject(sub); 
   } 

   public Subject getSubj() { 
      return subj; 
   } 

   public String getName() { 
      return name; 
   } 

   public void setName(String s) { 
      name = s; 
   } 

   /** 
    * 重寫clone()方法 
    * 
    * @return 
    */ 
   public Object clone() { 
      // 深拷貝,創(chuàng)建拷貝類的一個新對象,這樣就和原始對象相互獨(dú)立
      Student s = new Student(name, subj.getName()); 
      return s; 
   } 
}

 

輸出結(jié)果如下:
  Original Object: John - Algebra
 Cloned Object: John - Algebra
 Is Original Object the same with Cloned Object: false
 Is Original Object's field name the same with Cloned Object: true
 Is Original Object's field subj the same with Cloned Object: false
 Original Object after it is updated: Dan - Physics
 Cloned Object after updating original object: John - Algebra

  很容易發(fā)現(xiàn)clone()方法中的一點(diǎn)變化。因為它是深拷貝,所以你需要創(chuàng)建拷貝類的一個對象。因為在Student類中有對象引用,所以需要在Student類中實現(xiàn)Cloneable接口并且重寫clone方法。

3、通過序列化實現(xiàn)深拷貝
    也可以通過序列化來實現(xiàn)深拷貝。序列化是干什么的?它將整個對象圖寫入到一個持久化存儲文件中并且當(dāng)需要的時候把它讀取回來, 這意味著當(dāng)你需要把它讀取回來時你需要整個對象圖的一個拷貝。這就是當(dāng)你深拷貝一個對象時真正需要的東西。請注意,當(dāng)你通過序列化進(jìn)行深拷貝時,必須確保對象圖中所有類都是可序列化的。
 

復(fù)制代碼代碼如下:

public class ColoredCircle implements Serializable { 
   private int x; 
   private int y; 

   public ColoredCircle(int x, int y) { 
      this.x = x; 
      this.y = y; 
   } 

   public int getX() { 
      return x; 
   } 

   public void setX(int x) { 
      this.x = x; 
   } 

   public int getY() { 
      return y; 
   } 

   public void setY(int y) { 
      this.y = y; 
   } 

   @Override 
   public String toString() { 
      return "x=" + x + ", y=" + y; 
   } 
}
public class DeepCopy {

   public static void main(String[] args) throws IOException { 
      ObjectOutputStream oos = null; 
      ObjectInputStream ois = null; 

      try { 
         // 創(chuàng)建原始的可序列化對象 
         ColoredCircle c1 = new ColoredCircle(100, 100); 
         System.out.println("Original = " + c1); 

         ColoredCircle c2 = null; 

         // 通過序列化實現(xiàn)深拷貝 
         ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
         oos = new ObjectOutputStream(bos); 
         // 序列化以及傳遞這個對象 
         oos.writeObject(c1); 
         oos.flush(); 
         ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray()); 
         ois = new ObjectInputStream(bin); 
         // 返回新的對象 
         c2 = (ColoredCircle) ois.readObject(); 

         // 校驗內(nèi)容是否相同 
         System.out.println("Copied   = " + c2); 
         // 改變原始對象的內(nèi)容 
         c1.setX(200); 
         c1.setY(200); 
         // 查看每一個現(xiàn)在的內(nèi)容 
         System.out.println("Original = " + c1); 
         System.out.println("Copied   = " + c2); 
      } catch (Exception e) { 
         System.out.println("Exception in main = " + e); 
      } finally { 
         oos.close(); 
         ois.close(); 
      } 
   } 
}

 

輸出結(jié)果如下:
 Original = x=100, y=100 
 Copied = x=100, y=100 
 Original = x=200, y=200 
 Copied = x=100, y=100
這里,你只需要做以下幾件事兒:
(1)確保對象圖中的所有類都是可序列化的
(2)創(chuàng)建輸入輸出流
(3)使用這個輸入輸出流來創(chuàng)建對象輸入和對象輸出流
(4)將你想要拷貝的對象傳遞給對象輸出流
(5)從對象輸入流中讀取新的對象并且轉(zhuǎn)換回你所發(fā)送的對象的類

    在這個例子中,我創(chuàng)建了一個ColoredCircle對象c1然后將它序列化 (將它寫到ByteArrayOutputStream中). 然后我反序列化這個序列化后的對象并將它保存到c2中。隨后我修改了原始對象c1。然后結(jié)果如你所見,c1不同于c2,對c1所做的任何修改都不會影響c2。

注意,序列化這種方式有其自身的限制和問題:
因為無法序列化transient變量, 使用這種方法將無法拷貝transient變量。

   再就是性能問題。創(chuàng)建一個socket, 序列化一個對象, 通過socket傳輸它, 然后反序列化它,這個過程與調(diào)用已有對象的方法相比是很慢的。所以在性能上會有天壤之別。如果性能對你的代碼來說是至關(guān)重要的,建議不要使用這種方式。它比通過實現(xiàn)Clonable接口這種方式來進(jìn)行深拷貝幾乎多花100倍的時間。

四、延遲拷貝
   延遲拷貝是淺拷貝和深拷貝的一個組合,實際上很少會使用。 當(dāng)最開始拷貝一個對象時,會使用速度較快的淺拷貝,還會使用一個計數(shù)器來記錄有多少對象共享這個數(shù)據(jù)。當(dāng)程序想要修改原始的對象時,它會決定數(shù)據(jù)是否被共享(通過檢查計數(shù)器)并根據(jù)需要進(jìn)行深拷貝。 
   延遲拷貝從外面看起來就是深拷貝,但是只要有可能它就會利用淺拷貝的速度。當(dāng)原始對象中的引用不經(jīng)常改變的時候可以使用延遲拷貝。由于存在計數(shù)器,效率下降很高,但只是常量級的開銷。而且, 在某些情況下, 循環(huán)引用會導(dǎo)致一些問題。

五、如何選擇
  如果對象的屬性全是基本類型的,那么可以使用淺拷貝,但是如果對象有引用屬性,那就要基于具體的需求來選擇淺拷貝還是深拷貝。我的意思是如果對象引用任何時候都不會被改變,那么沒必要使用深拷貝,只需要使用淺拷貝就行了。如果對象引用經(jīng)常改變,那么就要使用深拷貝。沒有一成不變的規(guī)則,一切都取決于具體需求。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 三级aaa黄特色 | tube性睡觉hd | 日本一区二区三区视频在线观看 | 美女被上漫画 | 欧美日韩不卡视频 | 久热这里在线精品 | 亚洲性视频在线观看 | 欧美在线一级视频 | 色无月| 午夜在线观看免费完整直播网页 | poronovideos极度变态 | 男人的天堂在线观看免费 | caoporm国产精品视频免费 | 日韩欧美国产综合精品 | 精品日韩欧美一区二区三区 | 亚洲性夜 | 色婷婷久 | 国产a不卡片精品免费观看 国产aaa伦理片 | 亚欧洲乱码专区视频 | 日韩亚洲一区中文字幕在线 | 日本xxxx69hd| 国产馆精品推荐在线观看 | 亚洲AV永久无码精品澳门 | 91精品大神国产在线播放 | 大陆国语自产精品视频在 | 丝瓜茄子绿巨人秋葵榴莲污 | 我在厨房摸岳的乳HD在线观看 | 日本高清免费不卡在线播放 | 人人精品久久 | 麻豆视频免费在线观看 | 免费精品视频在线 | 亚洲va久久久噜噜噜久久狠狠 | 日本丰满www色 | 精品蜜臀AV在线天堂 | 性欧美xxxxx老太婆 | 99国产自偷色久 | www亚洲视频 | 男人使劲躁女人小视频 | 欧美a大片| 天天干天天爽天天操 | 538亚洲欧美国产日韩在线精品 |