Deep Copy (Deep Copy) และ Shallow Copy (สำเนาตื้น) เป็นสองแนวคิดที่ใช้กันทั่วไป โดยเฉพาะในภาษา C++ หากคุณไม่เข้าใจ คุณจะประสบปัญหาในการลบ แต่โชคดีที่เราใช้ Java ที่นี่ แม้ว่า Java จะจัดการการรีไซเคิลอ็อบเจ็กต์โดยอัตโนมัติ แต่เรายังคงต้องให้ความสนใจเพียงพอกับสำเนาเชิงลึก (สำเนาเชิงลึก) และสำเนาตื้น (สำเนาตื้น) เพราะบางครั้งแนวคิดทั้งสองนี้มักจะทำให้เราสับสนอย่างมาก
การคัดลอกแบบตื้นหมายความว่าเมื่อคัดลอกวัตถุ เฉพาะวัตถุเอง (รวมถึงตัวแปรพื้นฐานในวัตถุ) เท่านั้นที่ถูกคัดลอก แต่วัตถุที่ชี้ไปโดยการอ้างอิงที่มีอยู่ในวัตถุจะไม่ถูกคัดลอก การทำสำเนาเชิงลึกไม่เพียงแต่คัดลอกวัตถุเท่านั้น แต่ยังคัดลอกวัตถุทั้งหมดที่ชี้ไปโดยการอ้างอิงที่มีอยู่ในวัตถุด้วย ตัวอย่างเช่น เพื่อให้ชัดเจนยิ่งขึ้น: วัตถุ A1 มีการอ้างอิงถึง B1 และ B1 มีการอ้างอิงถึง C1 สำเนาตื้น A1 เพื่อรับ A2 A2 ยังคงมีการอ้างอิงถึง B1 และ B1 ยังคงมีการอ้างอิงถึง C1 Deep Copy เป็นการเรียกซ้ำของ Deep Copy A1 เพื่อรับ A2 มีการอ้างอิงถึง B2 (สำเนาของ B1) และ B2 มีการอ้างอิงถึง C2 (สำเนาของ C1)
หากไม่ได้เขียนเมธอด clone() ใหม่ วัตถุที่ได้รับจากการเรียกเมธอดนี้จะเป็นสำเนาแบบตื้น เรามาเน้นที่การคัดลอกแบบลึกด้านล่าง
เรียกใช้โปรแกรมต่อไปนี้เพื่อดูสำเนาแบบตื้น:
คลาส Professor0 ดำเนินการ Cloneable { String name; int age; Professor0(String name, int age) { this.name = name; this.age = age; } public Object clone() พ่น CloneNotSupportedException { return super.clone(); คลาส Student0 ดำเนินการ Cloneable { ชื่อสตริง; // วัตถุคงที่ int age; Professor0 p;//ค่าอ้างอิงของนักเรียน 1 และนักเรียน 2 เท่ากัน Student0 (ชื่อสตริง, อายุ int, Professor0 p) { this.name = name; this.age = age; this.p = p; } public Object clone() { Student0 o = null; .clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } กลับ o; } } คลาสสาธารณะ ShallowCopy { โมฆะคงที่สาธารณะ main(String[] args) { Professor0 p = new Professor0("wangwu", 50); Student0 s1 = ใหม่ Student0("zhangsan", 18, p); Student0 s2 = (Student0) s1.clone(); p.name = "lisi"; s2.p.age = 30; s2.name = "z"; System.out.println("ชื่อนักเรียน s1:" + s1.name + "/n ชื่อศาสตราจารย์ของนักเรียน s1:" + s1.p.name + "," + "/n อายุของศาสตราจารย์ของนักเรียน s1" + s1 .p.age);//ศาสตราจารย์ของนักศึกษา 1} }
s2 เปลี่ยนไป แต่ s1 ก็เปลี่ยนไปเช่นกัน โดยพิสูจน์ว่า p ของ s1 และ p ของ s2 ชี้ไปที่วัตถุเดียวกัน นี่ไม่ใช่กรณีในความต้องการที่แท้จริงของเรา ดังนั้นเราจึงต้องมีสำเนาโดยละเอียด:
ศาสตราจารย์คลาสใช้ Cloneable { String name; int age; Professor (String name, int age) { this.name = name; this.age = age; } public Object clone() { Object o = null; clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString() } ส่งคืน o; } } คลาส นักเรียนใช้ Cloneable { ชื่อสตริง; int age; Professor p; Student (ชื่อสตริง, int age, ศาสตราจารย์ p) { this.name = name; this.age = age; this.p = p; } public Object clone() o = (นักเรียน) super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } op = (ศาสตราจารย์) p.clone(); return o; } } คลาสสาธารณะ DeepCopy { public static void main(String args[]) { long t1 = System.currentTimeMillis(); ศาสตราจารย์ p = ศาสตราจารย์ใหม่ ("wangwu", 50); = นักเรียนใหม่ ("zhangsan", 18, p); นักเรียน s2 = (นักเรียน) s1.clone(); s2.p.name = "lisi"; s2.p.age = 30; System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); // อาจารย์ของนักเรียนคนที่ 1 ไม่เปลี่ยนแปลง ยาว t2 = System.currentTimeMillis(); System.out.println(t2-t1);
แน่นอนว่า เรายังมีวิธี deep copy อีกด้วย ซึ่งก็คือการทำให้วัตถุเป็นอนุกรม:
import java.io.*; // การทำให้เป็นอนุกรมเป็นคลาสที่ใช้เวลานาน Professor2 (ชื่อสตริง, int age) { this. name = name; this.age = age; } } คลาส Student2 ใช้ Serializable { /** * */ private static สุดท้าย serialVersionUID = 1L; วัตถุคงที่ int age; Professor2 p;//ค่าอ้างอิงของนักเรียนคนที่ 1 และนักเรียนคนที่ 2 เท่ากัน Student2(String name, int age, Professor2 p) { this.name = name; this.age = age; this.p = p; } วัตถุสาธารณะ deepClone() พ่น IOException, OptionalDataException, ClassNotFoundException { // เขียนวัตถุไปที่ สตรีมใน ByteArrayOutputStream bo = ByteArrayOutputStream ใหม่ (); ObjectOutputStream oo = ObjectOutputStream ใหม่ (bo); oo.writeObject(this); // อ่าน ByteArrayInputStream จากสตรีม bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return (oi.readObject() } } /** * @param args */ public static void main(String[] args) พ่น OptionalDataException, IOException, ClassNotFoundException { long t1 = System.currentTimeMillis(); Professor2 p = new Professor2("wangwu", 50); Student2 s1 = ใหม่ Student2("zhangsan", 18, p); Student2 s2 = (Student2) s1.deepClone (); s2.p.name = "lisi"; System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); // อาจารย์ของนักเรียน 1 ไม่เปลี่ยนแปลง ยาว t2 = System.currentTimeMillis(); System.out.println(t2-t1);
อย่างไรก็ตาม การทำให้เป็นอนุกรมนั้นใช้เวลานานมาก ในบางเฟรมเวิร์ก เรารู้สึกว่ามันมักจะทำให้อ็อบเจ็กต์เป็นอนุกรมแล้วจึงถ่ายโอน ซึ่งใช้เวลานานมาก