เป็นการยากที่จะใช้ฟังก์ชั่นการคัดลอกวัตถุลึก ขั้นตอนใดที่คุณต้องทำเพื่อให้แน่ใจว่าวัตถุดั้งเดิมและสิ่งที่โคลนแชร์ไม่มีการอ้างอิง?
เป็นการยากที่จะใช้ฟังก์ชั่นการคัดลอกวัตถุลึก ขั้นตอนใดที่คุณต้องทำเพื่อให้แน่ใจว่าวัตถุดั้งเดิมและสิ่งที่โคลนแชร์ไม่มีการอ้างอิง?
คำตอบ:
วิธีที่ปลอดภัยคือการทำให้เป็นอันดับวัตถุแล้วยกเลิกการเรียงลำดับ สิ่งนี้ทำให้มั่นใจได้ว่าทุกอย่างเป็นการอ้างอิงใหม่
นี่คือบทความเกี่ยวกับวิธีการทำสิ่งนี้อย่างมีประสิทธิภาพ
Caveats: เป็นไปได้สำหรับคลาสที่จะแทนที่การทำให้เป็นอนุกรมซึ่งอินสแตนซ์ใหม่จะไม่ถูกสร้างขึ้นเช่นสำหรับซิงเกิลตัน แน่นอนว่านี่ไม่ได้ผลถ้าคลาสของคุณไม่ต่อเนื่องกัน
Object.clone()
ไม่กี่คนที่ได้กล่าวถึงการใช้หรือการเอาชนะ อย่าทำมัน Object.clone()
มีปัญหาที่สำคัญบางอย่างและการใช้งานไม่ได้รับการสนับสนุนในกรณีส่วนใหญ่ โปรดดูรายการที่ 11 จาก " มีผลบังคับใช้ Java " โดยโจชัวโบลชหาคำตอบที่สมบูรณ์ ฉันเชื่อว่าคุณสามารถใช้Object.clone()
กับอาเรย์แบบดั้งเดิมได้อย่างปลอดภัยแต่นอกเหนือจากนั้นคุณต้องมีความรอบคอบในการใช้และการแทนที่อย่างถูกต้อง
โครงร่างที่ขึ้นอยู่กับการทำให้เป็นอันดับ (XML หรืออื่น ๆ ) เป็น kludgy
ไม่มีคำตอบง่าย ๆ ที่นี่ หากคุณต้องการคัดลอกวัตถุที่ลึกคุณจะต้องสำรวจกราฟวัตถุและคัดลอกวัตถุลูกแต่ละรายการอย่างชัดเจนผ่านตัวสร้างสำเนาของวัตถุหรือวิธีการโรงงานคงที่ที่จะคัดลอกวัตถุลูกลึก ไม่String
สามารถคัดลอกImmutables (เช่น) ได้ คุณควรสนับสนุนความไม่เปลี่ยนรูปด้วยเหตุผลนี้
คุณสามารถทำสำเนาลึกด้วยซีเรียลไลซ์เซชั่นโดยไม่ต้องสร้างไฟล์
implement serializable
วัตถุของคุณคุณต้องการคัดลอกลึกจะต้อง หากชั้นเรียนไม่เป็นที่สิ้นสุดหรือไม่สามารถแก้ไขได้ให้ขยายชั้นเรียนและใช้งานได้
แปลงคลาสของคุณเป็นจำนวนไบต์:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
กู้คืนคลาสของคุณจากกระแสไบต์:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
instance
ในกรณีนี้?
คุณสามารถทำการโคลนแบบลึกที่ใช้การทำซีเรียลorg.apache.commons.lang3.SerializationUtils.clone(T)
ไลเซชันโดยใช้ใน Apache Commons Lang แต่ต้องระวัง - ประสิทธิภาพเป็นสุดยอด
โดยทั่วไปแล้ววิธีที่ดีที่สุดคือการเขียนวิธีการโคลนของคุณเองสำหรับแต่ละคลาสของวัตถุในกราฟวัตถุที่ต้องการการโคลน
org.apache.commons.lang.SerializationUtils
วิธีหนึ่งที่จะใช้การทำสำเนาลึกคือการเพิ่มตัวสร้างสำเนาให้กับแต่ละคลาสที่เกี่ยวข้อง ตัวสร้างการคัดลอกใช้อินสแตนซ์ของ 'this' เป็นอาร์กิวเมนต์เดียวและคัดลอกค่าทั้งหมดจากมัน ค่อนข้างทำงาน แต่ค่อนข้างตรงไปตรงมาและปลอดภัย
แก้ไข: โปรดทราบว่าคุณไม่จำเป็นต้องใช้วิธีการเข้าถึงเพื่ออ่านฟิลด์ คุณสามารถเข้าถึงฟิลด์ทั้งหมดได้โดยตรงเนื่องจากอินสแตนซ์ต้นทางเป็นประเภทเดียวกันเสมอกับอินสแตนซ์ที่มีตัวสร้างการคัดลอก ชัดเจน แต่อาจถูกมองข้าม
ตัวอย่าง:
public class Order {
private long number;
public Order() {
}
/**
* Copy constructor
*/
public Order(Order source) {
number = source.number;
}
}
public class Customer {
private String name;
private List<Order> orders = new ArrayList<Order>();
public Customer() {
}
/**
* Copy constructor
*/
public Customer(Customer source) {
name = source.name;
for (Order sourceOrder : source.orders) {
orders.add(new Order(sourceOrder));
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
แก้ไข: โปรดทราบว่าเมื่อใช้ตัวสร้างสำเนาคุณต้องทราบประเภทรันไทม์ของวัตถุที่คุณกำลังคัดลอก ด้วยวิธีการข้างต้นคุณไม่สามารถคัดลอกรายการแบบผสมได้อย่างง่ายดาย
Toyota
รหัสของคุณจะใส่Car
ในรายการปลายทาง การโคลนนิ่งที่เหมาะสมนั้นโดยทั่วไปต้องการคลาสที่จัดเตรียมวิธีโรงงานเสมือนซึ่งสัญญาระบุว่าจะส่งคืนออบเจ็กต์ใหม่ของคลาสของตัวเอง ตัวสร้างสำเนาตัวเองควรจะprotected
ตรวจสอบให้แน่ใจว่ามันจะถูกใช้เพื่อสร้างวัตถุที่มีชนิดที่ตรงกับวัตถุที่ถูกคัดลอกเท่านั้น)
คุณสามารถใช้ไลบรารีที่มี API อย่างง่ายและทำการโคลนอย่างรวดเร็วด้วยการสะท้อน (ควรเร็วกว่าวิธีการทำให้เป็นอนุกรม)
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Apache Commons เสนอวิธีที่รวดเร็วในการโคลนวัตถุ
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
XStream มีประโยชน์จริง ๆ ในกรณีดังกล่าว นี่คือรหัสง่าย ๆ ที่จะทำการโคลน
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
วิธีการหนึ่งที่ง่ายและง่ายมากคือการใช้ Jackson JSON เพื่อทำให้เป็นอนุกรม Java Object ที่ซับซ้อนกับ JSON และอ่านมันกลับมา
สำหรับผู้ใช้Spring Framework ใช้คลาสorg.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
สำหรับวัตถุที่ซับซ้อนและเมื่อประสิทธิภาพไม่สำคัญฉันใช้ json ไลบรารี่เช่นgson เพื่อทำให้วัตถุเป็นอนุกรมข้อความ json, จากนั้น deserialize ข้อความเพื่อให้ได้วัตถุใหม่
gson ซึ่งอยู่บนพื้นฐานของการสะท้อนจะงานในกรณีส่วนใหญ่ยกเว้นว่าเขตจะไม่สามารถคัดลอกและวัตถุที่มีการอ้างอิงวงกลมที่มีสาเหตุtransient
StackOverflowError
public static <T> T copy(T anObject, Class<T> classInfo) {
Gson gson = new GsonBuilder().create();
String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);
return newObject;
}
public static void main(String[] args) {
String originalObject = "hello";
String copiedObject = copy(originalObject, String.class);
}
ใช้ XStream ( http://x-stream.github.io/ ) คุณสามารถควบคุมคุณสมบัติที่คุณสามารถละเว้นผ่านคำอธิบายประกอบหรือระบุชื่อคุณสมบัติให้กับคลาส XStream อย่างชัดเจน ยิ่งกว่านั้นคุณไม่จำเป็นต้องใช้อินเทอร์เฟซแบบ clonable
การทำสำเนาลึกสามารถทำได้เฉพาะเมื่อได้รับความยินยอมจากแต่ละชั้นเรียน หากคุณมีการควบคุมลำดับชั้นของชั้นเรียนแล้วคุณสามารถใช้ส่วนต่อประสาน clonable และใช้วิธีการโคลน มิฉะนั้นการทำสำเนาที่ลึกนั้นเป็นไปไม่ได้ที่จะทำอย่างปลอดภัยเพราะวัตถุอาจแชร์ทรัพยากรที่ไม่ใช่ข้อมูล (เช่นการเชื่อมต่อฐานข้อมูล) โดยทั่วไปแล้วการทำสำเนาแบบลึกถือว่าเป็นการใช้งานที่ไม่เหมาะสมในสภาพแวดล้อม Java และควรหลีกเลี่ยงผ่านวิธีการออกแบบที่เหมาะสม
import com.thoughtworks.xstream.XStream;
public class deepCopy {
private static XStream xstream = new XStream();
//serialize with Xstream them deserialize ...
public static Object deepCopy(Object obj){
return xstream.fromXML(xstream.toXML(obj));
}
}
BeanUtilsทำหน้าที่โคลนนิ่งลึกได้ดีมาก
BeanUtils.cloneBean(obj);
1)
public static Object deepClone(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
2)
// (1) create a MyPerson object named Al
MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
MyPerson al = new MyPerson("Al", "Arun", address);
// (2) make a deep clone of Al
MyPerson neighbor = (MyPerson)deepClone(al);
นี่คือ MyPerson และ MyAddress class ของคุณจะต้องติดตั้งส่วนต่อประสานที่ปลอดภัย
การใช้แจ็คสันเพื่อทำให้เป็นอันดับและทำให้เป็นวัตถุเป็นอนุกรม การใช้งานนี้ไม่ต้องการวัตถุเพื่อใช้คลาส Serializable
<T> T clone(T object, Class<T> clazzType) throws IOException {
final ObjectMapper objMapper = new ObjectMapper();
String jsonStr= objMapper.writeValueAsString(object);
return objMapper.readValue(jsonStr, clazzType);
}