このインターフェイス Cloneable が Java に存在することはわかっています。このインターフェイスを実装するクラスは、コピーする機能を備えています。同時に、パフォーマンスの点では、新しいオブジェクトを介してオブジェクトを直接生成するよりも高速です。特に大きなオブジェクトの場合、パフォーマンスの向上は非常に明白です。しかし、コピーには深いコピーと浅いコピーがあることがわかっていますが、浅いコピーにはオブジェクトの属性が不完全にコピーされるという問題があります。深いコピーと浅いコピーについては、こちらを参照してください: Java の浅いコピーと深いコピーの段階的な分析
1. 浅いコピーの問題
まず次のコードを見てみましょう:
public class Person implements Cloneable{ /** 姓名 **/ private String name; /** 电子邮件 **/ private Email email; public String getName() { return name; } public void setName(String name) { this.name = name; } public Email getEmail() { return email; } public void setEmail(Email email) { this.email = email; } public Person(String name,Email email){ this.name = name; this.email = email; } public Person(String name){ this.name = name; } protected Person clone() { Person person = null; try { person = (Person) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; } } public class Client { public static void main(String[] args) { //写封邮件 Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); Person person1 = new Person("张三",email); Person person2 = person1.clone(); person2.setName("李四"); Person person3 = person1.clone(); person3.setName("王五"); System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent()); System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent()); System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent()); } } -------------------- Output: 张三的邮件内容是:请与今天12:30到二会议室参加会议... 李四的邮件内容是:请与今天12:30到二会议室参加会议... 王五的邮件内容是:请与今天12:30到二会议室参加会议...
このアプリケーションでは、最初に電子メールを定義します。次に、Zhang San、Li Si、Wang Wu に電子メールを送信します。彼らは同じ電子メールを使用しており、名前が異なるだけなので、Zhang San のオブジェクト クラスを使用して Li Si と Wang Wu のオブジェクトをコピーし、名前を変更します。できる。ここまでのプログラムに問題はありませんが、Zhang San が 30 分前に到着する必要がある場合は、メールの内容を変更します:
public class Client { public static void main(String[] args) { //写封邮件 Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); Person person1 = new Person("张三",email); Person person2 = person1.clone(); person2.setName("李四"); Person person3 = person1.clone(); person3.setName("王五"); person1.getEmail().setContent("请与今天12:00到二会议室参加会议..."); System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent()); System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent()); System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent()); } }
ここでも、Zhang San のオブジェクトを使用して Li Si をコピーします。張三のメールの内容は次のように変わりました。会議に参加するために、今日の 12:00 に第 2 会議室に来てください...しかし、結果は次のとおりです:
张三的邮件内容是:请与今天12:00到二会议室参加会议... 李四的邮件内容是:请与今天12:00到二会议室参加会议... 王五的邮件内容是:请与今天12:00到二会议室参加会议...
ここで、なぜ Li Si と Wang Wu のメールの内容も変更されたのか疑問に思います。 30分前に到着するように言われたら反対するでしょう。
実際、問題の鍵は clone() メソッドにあります。 clone() メソッドが Object クラスの clone() メソッドを使用していることはわかっていますが、このメソッドには欠陥があります。基本的なルールは次のとおりです:
1. 基本型
変数が基本型の場合、その値 (int、float など) をコピーします。
2. オブジェクト
変数がインスタンス オブジェクトの場合、そのアドレス参照をコピーします。これは、この時点で新しいオブジェクトと元のオブジェクトがインスタンス変数を共有することを意味します。
3. 文字列
変数が文字列の場合は、そのアドレス参照をコピーします。ただし、変更すると、文字列プールから新しい文字列が再生成され、元の Ziducheng オブジェクトは変更されません。
上記のルールに基づいて、3 人が共通のオブジェクトを共有している場合、Li Si と Wang Wu もそれを変更するため、上記の状況が発生します。この状況は、 clone() メソッドで新しいオブジェクトを作成するだけで解決できます。その後、Zhang San がそのオブジェクトを参照します。
protected Person clone() { Person person = null; try { person = (Person) super.clone(); person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent())); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; }
つまり、浅いコピーは、使いやすい Java によって提供される単なるコピー メカニズムです。直接。
上記の解決策にはまだ問題があり、コピーによって生成されたオブジェクトがシステム内に多数ある場合、クラスごとに clone() メソッドを作成する場合は、ディープ コピーを実行する必要もあります。多数の新しいオブジェクトを作成します。このプロジェクトは非常に大きいため、シリアル化を使用してオブジェクトをコピーできます。
2. シリアル化を使用してオブジェクトをコピーします
シリアル化を使用してオブジェクトをコピーするにはどうすればよいですか?これは、メモリ内のバイト ストリームをコピーすることで比較的簡単に実現できます。親オブジェクトをバイト ストリームに書き込み、それをバイト ストリームから読み出すことで、新しいオブジェクトを作成できます。新しいオブジェクトと親オブジェクトの間の参照共有の問題は発生しません。実際には、 のディープ コピーを実装します。オブジェクト。
public class CloneUtils { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T obj){ T cloneObj = null; try { //写入字节流 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new ObjectOutputStream(out); obs.writeObject(obj); obs.close(); //分配内存,写入原始对象,生成新对象 ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ios); //返回生成的新对象 cloneObj = (T) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } }
このツール クラスを使用するオブジェクトは Serializable インターフェイスを実装する必要があります。そうしないとクローンを作成できません。
public class Person implements Serializable{ private static final long serialVersionUID = 2631590509760908280L; .................. //去除clone()方法 } public class Email implements Serializable{ private static final long serialVersionUID = 1267293988171991494L; .................... }
したがって、このツール クラスを使用するオブジェクトが Serializable インターフェイスを実装している限り、オブジェクトのクローンを作成でき、 clone() メソッドを実装するために Cloneable インターフェイスを継承する必要はありません。
public class Client { public static void main(String[] args) { //写封邮件 Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); Person person1 = new Person("张三",email); Person person2 = CloneUtils.clone(person1); person2.setName("李四"); Person person3 = CloneUtils.clone(person1); person3.setName("王五"); person1.getEmail().setContent("请与今天12:00到二会议室参加会议..."); System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent()); System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent()); System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent()); } } ------------------- Output: 张三的邮件内容是:请与今天12:00到二会议室参加会议... 李四的邮件内容是:请与今天12:30到二会议室参加会议... 王五的邮件内容是:请与今天12:30到二会议室参加会议...
上記は Java 改善パート 5 の内容です-----オブジェクトをコピーするためのシリアル化の使用 その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。