Agak sulit untuk menerapkan fungsi penyalinan objek dalam. Langkah apa yang Anda ambil untuk memastikan objek asli dan bagian hasil kloning tidak memiliki referensi?
Agak sulit untuk menerapkan fungsi penyalinan objek dalam. Langkah apa yang Anda ambil untuk memastikan objek asli dan bagian hasil kloning tidak memiliki referensi?
Jawaban:
Cara yang aman adalah membuat serialisasi objek, lalu deserialize. Ini memastikan semuanya adalah referensi baru.
Inilah artikel tentang cara melakukan ini secara efisien.
Peringatan: Mungkin bagi kelas untuk mengganti serialisasi sehingga instance baru tidak dibuat, misalnya untuk lajang. Juga ini tentu saja tidak berfungsi jika kelas Anda tidak Serializable.
Beberapa orang telah menyebutkan penggunaan atau penggantian Object.clone()
. Jangan lakukan itu. Object.clone()
memiliki beberapa masalah utama, dan penggunaannya tidak disarankan dalam banyak kasus. Silakan lihat Butir 11, dari " Java Efektif " oleh Joshua Bloch untuk jawaban lengkap. Saya percaya Anda dapat dengan aman menggunakan Object.clone()
pada array tipe primitif, tetapi selain itu Anda harus bijaksana tentang penggunaan dan penggantian klon yang benar.
Skema yang mengandalkan serialisasi (XML atau yang lain) adalah kludgy.
Tidak ada jawaban yang mudah di sini. Jika Anda ingin menyalin objek secara mendalam, Anda harus melintasi grafik objek dan menyalin setiap objek anak secara eksplisit melalui copy constructor objek atau metode pabrik statis yang pada gilirannya akan menyalin objek anak secara mendalam. Kekekalan (mis. String
S) tidak perlu disalin. Sebagai tambahan, Anda harus mendukung ketidakberdayaan karena alasan ini.
Anda dapat membuat salinan dalam dengan serialisasi tanpa membuat file.
Objek yang ingin Anda salin dalam perlu implement serializable
. Jika kelas tidak final atau tidak dapat dimodifikasi, perluas kelas dan implementasikan serializable.
Konversi kelas Anda menjadi aliran byte:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
Pulihkan kelas Anda dari aliran byte:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
instance
dalam kasus ini?
Anda dapat melakukan klon mendalam berbasis serialisasi menggunakan org.apache.commons.lang3.SerializationUtils.clone(T)
di Apache Commons Lang, tapi hati-hati — kinerjanya buruk.
Secara umum, ini adalah praktik terbaik untuk menulis metode klon Anda sendiri untuk setiap kelas objek dalam grafik objek yang membutuhkan kloning.
org.apache.commons.lang.SerializationUtils
Salah satu cara untuk mengimplementasikan deep copy adalah dengan menambahkan copy constructor ke setiap kelas terkait. Pembuat salinan mengambil contoh 'ini' sebagai argumen tunggal dan menyalin semua nilai darinya. Cukup banyak pekerjaan, tetapi cukup mudah dan aman.
EDIT: perhatikan bahwa Anda tidak perlu menggunakan metode accessor untuk membaca bidang. Anda dapat mengakses semua bidang secara langsung karena instance sumber selalu dari tipe yang sama dengan instance dengan konstruktor salin. Jelas tapi mungkin terlewatkan.
Contoh:
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;
}
}
Sunting: Perhatikan bahwa saat menggunakan copy constructor Anda perlu mengetahui tipe runtime dari objek yang Anda salin. Dengan pendekatan di atas Anda tidak dapat dengan mudah menyalin daftar campuran (Anda mungkin dapat melakukannya dengan beberapa kode refleksi).
Toyota
, kode Anda akan dimasukkan ke Car
dalam daftar tujuan. Kloning yang tepat umumnya mensyaratkan bahwa kelas menyediakan metode pabrik virtual yang kontraknya menyatakan akan mengembalikan objek baru dari kelasnya sendiri; copy contructor itu sendiri harus protected
memastikan bahwa itu hanya akan digunakan untuk membangun objek yang tipe ketepatannya cocok dengan objek yang sedang disalin).
Anda dapat menggunakan perpustakaan yang memiliki API sederhana, dan melakukan kloning yang relatif cepat dengan refleksi (harus lebih cepat dari metode serialisasi).
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Apache commons menawarkan cara cepat untuk mengkloning objek secara mendalam.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
XStream sangat berguna dalam hal ini. Berikut ini adalah kode sederhana untuk melakukan kloning
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Salah satu pendekatan yang sangat mudah dan sederhana adalah dengan menggunakan Jackson JSON untuk membuat serialisasi Java Object yang kompleks ke JSON dan membacanya kembali.
Untuk pengguna Spring Framework . Menggunakan kelas org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
Untuk objek yang rumit dan ketika kinerjanya tidak signifikan saya menggunakan pustaka json, seperti gson untuk membuat serialisasi objek menjadi teks json, lalu menghapus teks untuk mendapatkan objek baru.
gson yang didasarkan pada refleksi akan bekerja dalam banyak kasus, kecuali transient
bidang yang tidak akan disalin dan objek dengan referensi melingkar dengan sebab 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);
}
Gunakan XStream ( http://x-stream.github.io/ ). Anda bahkan dapat mengontrol properti mana yang bisa Anda abaikan melalui anotasi atau secara eksplisit menentukan nama properti ke kelas XStream. Selain itu Anda tidak perlu mengimplementasikan antarmuka yang dapat dikloning.
Penyalinan yang dalam hanya dapat dilakukan dengan persetujuan masing-masing kelas. Jika Anda memiliki kontrol atas hierarki kelas maka Anda dapat mengimplementasikan antarmuka yang dapat dikloning dan mengimplementasikan metode Klon. Kalau tidak, melakukan penyalinan dalam tidak mungkin dilakukan dengan aman karena objek mungkin juga berbagi sumber daya non-data (mis. Koneksi basis data). Secara umum, penyalinan dalam dianggap praktik buruk di lingkungan Jawa dan harus dihindari melalui praktik desain yang sesuai.
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 melakukan pekerjaan yang sangat baik dalam mengkloning kacang.
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);
Di sini, kelas MyPerson dan MyAddress Anda harus mengimplementasikan antarmuka yang dapat diseret
Menggunakan Jackson untuk membuat cerita bersambung dan deserialisasi objek. Implementasi ini tidak memerlukan objek untuk mengimplementasikan kelas 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);
}