浅いコピーとは、オブジェクトをコピーするときに、オブジェクト自体(オブジェクトの基本変数を含む)のみがコピーされ、オブジェクトに含まれる参照が指すオブジェクトはコピーされないことを意味します。ディープコピーは、オブジェクト自体だけでなく、コピーオブジェクトに含まれる参照によってポイントされるすべてのオブジェクトもコピーします。たとえば、より明確です。オブジェクトA1にはB1への参照が含まれ、B1にはC1への参照が含まれます。A1の浅いコピーはA2を取得し、A2には引き続きB1への参照が含まれ、B1には引き続きC1への参照が含まれます。ディープコピーはシャローコピーの再帰であり、ディープコピーA1はA2を取得し、A2にはB2への参照(B1のコピー)が含まれ、B2にはC2への参照(C1のコピー)が含まれます。
次に、メモリ分析を使用して、ディープコピーとシャローコピーの違いを説明します。この記事では2つのコピーの違いに焦点を当てているため、メモリ分析ではヒープ内のオブジェクトのみが分析されます。
浅いコピー
class House implements Cloneable{
int size;
String address;
public House(int size, String address) {
this.size = size;
this.address = address;
}
public Object clone() throws CloneNotSupportedException{
return (Object)super.clone();
}
}
class Person implements Cloneable{
String name;
int age;
House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
public Person clone() {
Person o = null;
try {
o = (Person)super.clone();
}catch(Exception e) {
System.out.println(e);
}
return o;
}
}
public class CloneTest {
public static void main(String[] args) {
House house1 = new House(50, "平凡花苑");
Person person1 = new Person("Xiaoming", 20, house1);
System.out.println("改变前:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
//标记处
person2.house.size = 80;
person2.house.address = "梦想花苑";
System.out.println("改变后:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
}
}
// 改变前:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑
// 改变后:person1.name:Xiaoming person1.age:20 person1.house.size:80 person1.house.address:梦想花苑
マークの前
House house1 = new House(50, "平凡花苑");
Oxで始まるアドレスは、ヒープ内のオブジェクトのアドレスを表します。最初に、新しいhouse1オブジェクトが作成されます。houseオブジェクトの属性はすべて基本タイプであるため、他のオブジェクトを指す矢印はありません。
Person person1 = new Person("Xiaoming", 20, house1);
person1を作成する場合、オブジェクトのメンバー変数の1つが参照変数であるため、house1のアドレスはperson1に格納されます(したがって、house1のメンバー変数の変更は実際にはperson1の変数の外部で行われます)
Person person2 = person1.clone();
person1.clone()メソッドによって作成されたperson2には、person1のメンバー変数の基本タイプがすべて含まれています。Object.cloneメソッドは浅いコピーメソッドであるため、参照タイプのメンバー変数を同時にコピーすることはできません。 、したがって、person2の家はまだ元のhouse1のメモリアドレスを保存します
person2.name = "Meimei";
person2.age = 30;
person2の通常の型変数はperson1から完全に分離されているため、person2.nameとperson2.ageは、上記の変更後に変更されます。
マークの後
person2.house.size = 80;
person2.house.address = "梦想花苑";
このとき、person1とperson2は同じhouse1を指しているため、person2のhouse1のメンバー変数を変更すると、person1のhouse1のメンバー変数もそれに応じて変更されます。
ディープコピー
class House implements Cloneable{
int size;
String address;
public House(int size, String address) {
this.size = size;
this.address = address;
}
public Object clone() throws CloneNotSupportedException{
return (Object)super.clone();
}
}
class Person implements Cloneable{
String name;
int age;
House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
public Person clone() throws CloneNotSupportedException { //改变
Person o = null;
try {
o = (Person)super.clone();
}catch(Exception e) {
System.out.println(e);
}
o.house = (House) house.clone(); //主要改变
return o;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException { //改变
House house1 = new House(50, "平凡花苑");
Person person1 = new Person("Xiaoming", 20, house1);
System.out.println("改变前:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
//标记处
person2.house.size = 80;
person2.house.address = "梦想花苑";
System.out.println("改变后:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
}
}
// 改变前:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑
// 改变后:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑
マークの前
House house1 = new House(50, "平凡花苑");
Person person1 = new Person("Xiaoming", 20, house1);
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
上記のコードは基本的に浅いコピーと同じなので、あまり説明する必要はありません。主に次の違いを確認してください。
o.house = (House) house.clone(); //主要改变
浅いコピーと比較して、深いコピーには、person1.clone()と同様に、もう1行のコードがあります。これは、houseには基本タイプの変数しかないため、house.cloneは完全にコピーでき、この時点で取得したhouse2には何もありません。 house1で行います。
マークの後
person2.house.size = 80;
person2.house.address = "梦想花苑";
person2とhouse2は、プロトタイプ(person1とhouse1)とは関係がないことを示しているため、この時点でのhouseの操作は、person1のhouse1には影響しません。
展開
上記の紹介を通して、誰もがディープコピーとシャローコピーの違いを理解できるはずです。基本的に、ディープコピーはオブジェクト自体をコピーするだけでなく、コピーされたオブジェクトに含まれる参照によってポイントされるすべてのオブジェクトもコピーします。つまり、ディープコピーによって取得された2つのオブジェクトには交差がありません。印象を深めるために、house1の参照変数であるtv変数を引き続き導入し、person1とperson2が再び連絡を取り合うようにします。
class House implements Cloneable{
int size;
String address;
TV tv; //增加一个tv类
public House(int size, String address, TV tv) { //改变构造方法
this.size = size;
this.address = address;
this.tv = tv;
}
public Object clone() throws CloneNotSupportedException{
return (Object)super.clone();
}
}
//增加一个TV类
class TV{
int size;
String brand;
public TV(int size, String brand) {
this.size = size;
this.brand = brand;
}
}
class Person implements Cloneable{
String name;
int age;
House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
public Person clone() throws CloneNotSupportedException { //改变
Person o = null;
try {
o = (Person)super.clone();
}catch(Exception e) {
System.out.println(e);
}
o.house = (House) house.clone();
return o;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException { //改变
TV tv = new TV(65, "SONY"); //添加一个tv对象
House house1 = new House(50, "平凡花苑", tv); //改变
Person person1 = new Person("Xiaoming", 20, house1);
System.out.println("改变前:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address + " person1.house.tv.size:" + person1.house.tv.size + " person1.house.tv.brand:" + person1.house.tv.brand); //改变
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
person2.house.size = 80;
person2.house.address = "梦想花苑";
//标记处
person2.house.tv.size = 90; //添加
person2.house.tv.brand = "Xiaomi"; //添加
System.out.println("改变后:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address + " person1.house.tv.size:" + person1.house.tv.size + " person1.house.tv.brand:" + person1.house.tv.brand);
}
}
// 改变前:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑 person1.house.tv.size:65 person1.house.tv.brand:SONY
// 改变后:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑 person1.house.tv.size:90 person1.house.tv.brand:Xiaomi
マークの前
o.house = (House) house.clone();
このコード行により、house2はhouse1のすべての共通型変数を浅くコピーします。また、tvは参照型変数であり、house1のコピープロセスでは参照型変数のコピーがないため、house1とhouse2の両方が同じアドレスのテレビをポイントします。
マークの後
person2.house.tv.size = 90; //添加
person2.house.tv.brand = "Xiaomi"; //添加
この時点で、上記の変更後、house1とhouse2のtv属性が一緒に変更されたことは理解しやすいはずです。
o.tv = (TV) TV.clone();
上記のようなコードをhouseクラスに追加すると、結果が異なります。
上記の3つの実験を通じて、要約すると、ディープコピーを実現する場合は、最も外側の2つのオブジェクトが何の関係もなくなるまで、参照型変数を使用してすべてのオブジェクトを再帰的にコピーするだけで済みます。ディープコピーの理由は、参照型変数がオブジェクトに保存されるときに、対応するヒープアドレス(Oxで始まる)のみが保存され、Object.clone()メソッドはシャローコピーしか実行できないためです。