บทความนี้จะหารือเกี่ยวกับ 4 ประเด็นต่อไปนี้
1. อินเตอร์เฟส Java cloneable ใช้สำเนาลึก
2. Java Serialization ใช้สำเนาลึก
3. การวิเคราะห์ซอร์สโค้ดการโคลนไบนารีการคัดลอกที่เร็วที่สุดที่เร็วที่สุด
4. การเปรียบเทียบความเร็วของวิธีการคัดลอกหลายวิธี
ฉันจะไม่พูดถึงแนวคิดของการคัดลอกลึกในบทความนี้ การใช้สำเนาลึกใน C ++ โดยทั่วไปให้โอเวอร์โหลดตัวดำเนินการที่ได้รับมอบหมาย "=" เพื่อใช้สำเนาลึกระหว่างวัตถุในคลาสเดียวกัน ดังนั้นจึงเป็นเรื่องธรรมดาที่ใน Java เรายังสามารถกำหนดฟังก์ชั่นการคัดลอกเพื่อกำหนดคุณสมบัติแต่ละอย่างของวัตถุภายในฟังก์ชั่น วิธีนี้ง่ายและเป็นธรรมชาติ แต่มีปัญหาร้ายแรง: หากวันหนึ่งคุณลักษณะใหม่ที่ต้องใช้สำเนาลึกจะถูกเพิ่มลงในคลาสฟังก์ชันการคัดลอกที่เกี่ยวข้องจะต้องแก้ไขด้วย วิธีนี้นำมาซึ่งความไม่สะดวกอย่างมากต่อความสามารถในการขยายชั้นเรียน วิธีแก้ปัญหานี้มาดูวิธีการใช้งานของบทที่ 1, 2 และ 3 และการทดสอบความเร็วของส่วนที่ 4
1. อินเตอร์เฟส Java cloneable ใช้การคัดลอกลึก <br /> ด้วยวิธีนี้คลาสจะต้องใช้ฟังก์ชันโคลนอินเตอร์เฟส colnable และโทร super.clone ในฟังก์ชันโคลน สำเนาที่ลึกของวิธีนี้ยังนำปัญหาอื่นมาด้วย หากมีวัตถุของคลาสอื่น ๆ เป็นคุณสมบัติในชั้นเรียนคลาสอื่น ๆ จะต้องมีการโอเวอร์โหลดและนำไปใช้ในอินเทอร์เฟซ cloneable นี่คือตัวอย่าง ในตัวอย่างต่อไปนี้ ComplexDo มีวัตถุ Simpledo ในการใช้งาน Complexdo Deep Copy คุณต้องใช้อินเทอร์เฟซโคลนของ Simpledo ก่อน:
คลาสสาธารณะ Simpledo ใช้ cloneable, serializable {private int x = 1; สตริงส่วนตัว s = "Simpledo"; @Override วัตถุที่ได้รับการป้องกันโคลน () พ่น clonenotsupportedException {simpledo newClass = (simpledo) super.clone (); กลับ Newclass; }} คลาสสาธารณะคอมเพล็กซ์โดใช้ cloneable, serializable {private int x = 1; สตริงส่วนตัว s = "คอมเพล็กซ์"; จำนวนเต็มส่วนตัว A = 123; จำนวนเต็มส่วนตัว b = 1234; จำนวนเต็มส่วนตัว c = 1334455; สตริงส่วนตัว s2 = "hehehe"; สตริงส่วนตัว S3 = "ฮ่าฮ่าฮ่า"; ID ยาวส่วนตัว = 1233245L; ArrayList ส่วนตัว <Mimpledo> l = new ArrayList <Mimpledo> (); @Override วัตถุสาธารณะโคลน () พ่น clonenotsupportedException {ComplexDo newClass = (complexdo) super.clone (); newclass.l = arraylist ใหม่ <Mimpledo> (); สำหรับ (simpledo simple: this.l) {newclass.l.l.add ((simpledo) simple.clone ()); } ส่งคืน Newclass; - ควรสังเกตว่าบทความจำนวนมากบอกว่าผู้ดำเนินการที่ได้รับมอบหมายของประเภทสตริงเป็นสำเนาลึก แต่ในความเป็นจริงผู้ที่ใช้ตัวดำเนินการที่ได้รับมอบหมายใน Java เป็นสำเนาตื้น แต่ทำไมบทความที่มีข้อผิดพลาดที่ชัดเจนเช่นนี้ต้องบอกว่านี่เป็นสำเนาลึก? ความเข้าใจของฉันคือแอตทริบิวต์สตริงและประเภทเป็นประเภทพื้นฐานและวิธีการที่ให้ไว้จะเป็นวัตถุใหม่ตราบใดที่การเปลี่ยนแปลงข้อมูลภายในได้รับการออกแบบ ดังนั้นการดำเนินการสตริงจะไม่ส่งผลกระทบต่อหน่วยความจำที่ชี้ไป ดังนั้นการพูดโดยทั่วไปการดำเนินการที่ได้รับมอบหมายของคลาสพื้นฐานเช่นสตริงเป็นสำเนาลึก
ด้วยเหตุผลนี้เมื่อใช้การประกบสตริงสตริงจำเป็นต้องเปิดหน่วยความจำใหม่ผู้คนจำนวนมากแนะนำให้ใช้ StringBuilder แทนสตริงสำหรับการประกบเนื่องจาก StringBuilder จะ reapplyses หน่วยความจำขนาดใหญ่ขึ้นเมื่อช่วงอาร์เรย์ถ่านในตัวไม่เพียงพอ (สำหรับ JVM ที่ทันสมัย สำหรับการปลูกพืชตรงข้ามกับการประกบจะมีฟังก์ชั่นย่อยในสตริง เมื่อใช้ฟังก์ชัน Substring อาร์เรย์ถ่านภายในของสตริงใหม่เหมือนกับสตริงต้นฉบับหรือไม่? สิ่งนี้น่าสนใจยิ่งขึ้น หากคุณสนใจคุณสามารถเปรียบเทียบและตรวจสอบการใช้งาน JDK1.6 และ JKD1.7
2. Java Serialization ใช้สำเนาลึก
หลักการของวิธีนี้คือการใช้การทำให้เป็นอนุกรม Java เพื่อทำให้เป็นอนุกรมวัตถุในกระแสไบนารีไบต์จากนั้น deserialize และกำหนดค่าให้กับวัตถุ ตัวอย่างรหัส:
วัตถุสาธารณะ seircopy (วัตถุ src) {ลอง {byteArrayOutputStream byteout = ใหม่ byteArrayOutputStream (); ObjectOutputStream out = new ObjectOutputStream (byteout); out.writeObject (src); ByTeArrayInputStream Bytein = New ByteArrayInputStream (byteout.tobyteArray ()); ObjectInputStream ใน = New ObjectInputStream (Bytein); วัตถุ dest = in.readObject (); กลับมาแล้ว; } catch (exception e) {// ทำข้อผิดพลาดบางตัวจัดการส่งคืน null; - แน่นอนคุณยังสามารถใช้ JSON และห้องสมุดต่อเนื่องอื่น ๆ เพื่อทำให้อนุกรมเสร็จสมบูรณ์ วิธีนี้ช่วยหลีกเลี่ยงข้อบกพร่องที่ขยายได้อย่างมีประสิทธิภาพของอินเทอร์เฟซ Clonebel ฟังก์ชั่นสามารถเหมาะสำหรับทุกคลาส ข้อเสียคือการคัดลอกหน่วยความจำสัมพัทธ์ การทำให้เป็นอนุกรมจำเป็นต้องแปลงวัตถุเป็นลำธารไบนารีไบนารีก่อนแล้วจึง deserializing การคัดลอกลำธารไบนารีอีกครั้งเป็นชิ้นส่วนของหน่วยความจำวัตถุซึ่งค่อนข้างช้า
3. การวิเคราะห์ซอร์สโค้ดการโคลนไบนารีการคัดลอกที่เร็วที่สุดที่เร็วที่สุด
ในซอร์สโค้ดตรรกะการประมวลผลหลักอยู่ในคลาส Cloner
มีลิงก์ซ้ำสองลิงก์:
ใน (1) FastClone เสร็จสิ้นวัตถุที่สืบทอดมาจากคลาสอินเตอร์เฟส iFastClone นั่นคือพวกเขาทั้งหมดเป็นสำเนาของการดำเนินการรวบรวม
ใน (2) CloneObject เสร็จสิ้นกระบวนการของการได้รับแต่ละแอตทริบิวต์ของวัตถุปกติผ่านกลไกการสะท้อนกลับจากนั้นกำหนดค่าให้กับแอตทริบิวต์ของวัตถุที่สร้างขึ้นใหม่โดยใช้ objenesis
วิธีนี้สามารถขยายได้สูง ไม่เพียง แต่คุณสามารถพึ่งพารหัสที่มีอยู่เพื่อคัดลอกลึก แต่คุณยังสามารถกำหนดวิธีการโคลนนิ่งและประเภทที่ไม่จำเป็นต้องโคลนซึ่งมีความยืดหยุ่นสูง
4. การเปรียบเทียบความเร็วของวิธีการคัดลอกหลายวิธี
สามโหมดข้างต้นสามารถใช้ในการคัดลอกลึกและวิธีการคัดลอกที่เร็วที่สุดคือสิ่งที่เราใส่ใจ
ก่อนอื่นทดสอบรหัส:
Public Void TestClonecomplex () พ่น clonenotsupportedException {สุดท้าย int copycount = 1; รายการ <plexdo> complexDoList = new ArrayList <Complexdo> (copycount * 3); Final ComplexDo = new ComplexDo (); // การคำนวณ Library Long Long Start = System.currentTimeMillis (); สำหรับ (int i = 0; i <copycount; ++ i) {final complexdo deepClone = clner.deepClone (คอมเพล็กซ์); complexdolist.add (DeepClone); } Long End = System.currentTimeMillis (); System.out.println ("DEEPCLONE COST TIME =" + (จุดเริ่มต้น)); // การเรียกใช้ฟังก์ชันโคลนที่ใช้งานโดย cloneable interface start = system.currentTimeMillis (); สำหรับ (int i = 0; i <copycount; ++ i) {final complexdo interfaceClone = (complexdo) complex.clone (); complexdolist.add (interfaceclone); } end = system.currentTimeMillis (); System.out.println ("InterfaceClone cost time =" + (end-start)); // serialization และ deserialization สร้างวัตถุใหม่ start = system.currentTimeMillis (); สำหรับ (int i = 0; i <copycount; ++ i) {final complexdo seirclone = seircopy (คอมเพล็กซ์); complexdolist.add (Seirclone); } end = system.currentTimeMillis (); System.out.println ("Seirclone Cost Time =" + (end-start)); -หน่วยของผลลัพธ์การรันคือมิลลิวินาที (ข้อมูลนี้ถูกละเว้นและไม่ได้คำนวณฮอตสปอต Java และ GC ที่เป็นไปได้)
จากตารางนี้เราสามารถวาดข้อสรุป:
1. การคัดลอกอินเทอร์เฟซ cloneable นั้นเร็วที่สุดเพราะมันเกี่ยวข้องกับสำเนาหน่วยความจำเท่านั้น แต่ถ้าแอตทริบิวต์ที่เกี่ยวข้องเป็นวัตถุทั่วไปมากขึ้นมันเป็นเรื่องยากที่จะเขียน
2. การทำอนุกรม/deserialization สำเนาช้าที่สุด
3. การใช้ไลบรารีการโคลนนิ่งการคัดลอกของกลไกการเรียกซ้ำและการสะท้อนจะช้ากว่าการใช้งานอินเตอร์เฟส cloneable แต่เร็วกว่าวิธีการทำให้เป็นอนุกรม
ข้างต้นเป็นเรื่องเกี่ยวกับบทความนี้ฉันหวังว่ามันจะเป็นประโยชน์กับการเรียนรู้ของทุกคน