作成モード-プロトタイプモード
(I.概要
プロトタイプモードとは、作成済みのインスタンスをプロトタイプとして使用し、プロトタイプオブジェクトをコピーして、プロトタイプと同じまたは類似した新しいオブジェクトを作成することを指します。プロトタイプインスタンスは、作成するオブジェクトのタイプを指定します。オブジェクトの作成の詳細を知らなくても、この方法でオブジェクトを作成すると非常に効率的です。
プロトタイプモードは、メモリ内のバイナリストリームのコピーです。これは、直接の新しいオブジェクトのパフォーマンスよりもはるかに優れています。特に、コンストラクターがより複雑で、ループ本体で多数のオブジェクトが生成される場合、プロトタイプモードは非常に効率的です。
プロトタイプモードの浅いコピーと深いコピーについて言えば、浅いコピーは値型のメンバー変数のコピーであり、参照型の変数は参照のコピーにすぎないことは誰もが知っています。実際、2つのオブジェクトは依然として同じインスタンスを指しています。ディープコピーは、値型のメンバー変数をコピーするだけでなく、参照型のメンバー変数の記憶域にも適用され、新しいオブジェクトになります。これらを理解するだけでは十分ではありません。原則を理解する必要があります。実装プロセスを見てみましょう。
(2)浅いコピーバージョン
浅いコピーを実現するには、最初にCloneableインターフェースを実装する必要があります。このインターフェースにはメソッドがありません。その機能は、実行時にこのインターフェースを実装するクラスでclone()メソッドを使用しても安全であることを仮想マシンに通知することです。Java仮想マシンでは、このインターフェースを実装するクラスのみをコピーできます。
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private Sheep friend;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sheep;
}
}
次に、それをテストします。2つのハッシュコードの値を通じて、元のオブジェクトとクローンオブジェクトが同じオブジェクトではないことを見つけるのは難しくありません。これは、値型のメンバー変数がコピーされていることを示しています。
public class Test {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.setFriend(new Sheep("jack", 2, "黑色"));
Sheep sheep2 = (Sheep)sheep.clone();
System.out.println(sheep.hashCode()); //输出结果为 455896770
System.out.println(sheep2.hashCode()); //输出结果为 1323165413
}
}
別のテスト例を見ることができます。この例では、浅いコピーの問題を見つけることができます。2つのオブジェクトの参照型のフレンドが同じであることがわかりました。浅いコピーでは、参照が参照型にコピーされるだけで、新しい記憶域が開かれないことがわかります。したがって、この状況が発生した場合は、ディープコピーを使用する必要があります。
public class Test {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.setFriend(new Sheep("jack", 2, "黑色"));
Sheep sheep2 = (Sheep) sheep.clone();
System.out.println(sheep.getFriend().hashCode());//输出结果为 455896770
System.out.println(sheep2.getFriend().hashCode());//输出结果为 455896770
}
}
(3)ディープコピーバージョン
ディープコピーには2つのオプションがあります。1つは、参照型の属性を持つすべてのクラスにCloneableインターフェイスを実装することです。明らかに、参照型が多数ある場合、このメソッドはコードの量を急速に増加させ、同時にエラーの可能性を増やします。2つ目は、シリアル化と逆シリアル化によるディープコピーです。この方法はより便利ですが、すべての参照型属性もシリアル化可能でなければならないことに注意してください。そうしないと、このタイプのシリアル化は不可能になります。。
次に、上記の浅いコピーのクローンメソッドを変更して、ディープコピーにします。Cloneableインターフェースを実装する必要はありませんが、シリアライゼーションとデシリアライゼーションのニーズを満たすためにSerializableインターフェースを実装する必要があります。
public class Sheep implements Serializable {
private String name;
private int age;
private String color;
private Sheep friend;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Sheep) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
効果をテストしてみましょう。浅いコピーとは異なり、2つのオブジェクトの参照型は異なるオブジェクトを指します。テストの結果、逆シリアル化ではコンストラクターが呼び出されないことがわかりました。逆のシーケンスのオブジェクトは、構築メソッドによって生成されたのではなく、JVM自体によって生成されたオブジェクトです。
public class Test {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.setFriend(new Sheep("jack", 2, "黑色"));
Sheep sheep2 = (Sheep) sheep.deepClone();
System.out.println(sheep.getFriend().hashCode());//输出结果为 670576685
System.out.println(sheep2.getFriend().hashCode());//输出结果为 1323468230
}
}
2020年7月28日