JAva浅いコピーと深いコピー

6545 ワード

テキストリンク
1.ライトコピーとは
   浅いコピーは、元のオブジェクトのプロパティ値の正確なコピーを持つ新しいオブジェクトを作成します.属性が基本タイプである場合、コピーされるのは基本タイプの値です.プロパティがメモリアドレス(リファレンスタイプ)の場合、コピーはメモリアドレスです.そのため、オブジェクトの1つがこのアドレスを変更すると、別のオブジェクトに影響します.
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 { 
         //        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());
    }
}

出力結果は次のとおりです. 
 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
     この例では、コピーするクラスStudioにClonableインタフェースを実装し、Objectクラスのclone()メソッドを再書き込みし、メソッド内でsuper.clone()メソッドを呼び出します.出力結果から、元のオブジェクトstudの「name」プロパティの変更はコピーオブジェクトclonedStudioには影響しませんが、参照オブジェクトsubjの「name」プロパティの変更はコピーオブジェクトclonedStudioに影響します.
2、深いコピーとは
       深いコピーは、すべてのプロパティをコピーし、プロパティが指す動的に割り当てられたメモリをコピーします.オブジェクトが参照されているオブジェクトと一緒にコピーされると、深いコピーが発生します.深いコピーは、浅いコピーに比べて速度が遅く、コストがかかります.
実装1:
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() { 
      //    ,           ,            
      Student s = new Student(name, subj.getName()); 
      return s; 
   } 
}
実装2:
シーケンス化による深いコピー
シーケンス化によって深いコピーを実現することもできます.シーケンス化は何をしますか?オブジェクトマップ全体を永続化ストレージファイルに書き込み、必要に応じて読み込みます.これは、オブジェクトマップ全体のコピーが必要であることを意味します.これは、オブジェクトを深くコピーするときに本当に必要なものです.シーケンス化によって深くコピーする場合は、オブジェクトマップ内のすべてのクラスがシーケンス化可能であることを確認する必要があります.
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 { 
         //             
         ColoredCircle c1 = new ColoredCircle(100, 100); 
         System.out.println("Original = " + c1); 

         ColoredCircle c2 = null; 

         //            
         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(); 

         //          
         System.out.println("Copied   = " + c2); 
         //           
         c1.setX(200); 
         c1.setY(200); 
         //            
         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(); 
      } 
   } 
}

ここで、(1)オブジェクトマップ内のすべてのクラスが直列化可能であることを確認する(2)入出力ストリームの作成(3)この入出力ストリームを使用してオブジェクト入力とオブジェクト出力ストリームを作成する(4)コピーしたいオブジェクトをオブジェクト出力ストリームに渡す(5)オブジェクト入力ストリームから新しいオブジェクトを読み出し、送信したオブジェクトのクラスに変換する    この例では、ColoredCircleオブジェクトc 1を作成してシーケンス化(ByteArrayOutputStreamに書き込む)し、シーケンス化されたオブジェクトを逆シーケンス化してc 2に保存します.その後、元のオブジェクトc 1を変更しました.そして結果は、ご覧のように、c 1はc 2とは異なり、c 1に対する変更はc 2に影響しません.
シーケンス化には独自の制限と問題があります.transient変数をシーケンス化できないため、この方法ではtransient変数をコピーできません.
   さらにパフォーマンスの問題です.既存のオブジェクトを呼び出す方法に比べて遅いsocketを作成し、オブジェクトをシーケンス化し、socketを介して転送し、逆シーケンス化します.だから性能には雲泥の差がある.パフォーマンスがコードにとって重要である場合は、この方法を使用しないことをお勧めします.Clonableインタフェースを実装することによって深いコピーを行うよりもほぼ100倍の時間がかかります.