Artikel ini akan membahas 4 masalah berikut
1. Java Clonable Interface mengimplementasikan salinan yang dalam
2. Java Serialization mengimplementasikan salinan yang dalam
3. Analisis Kode Sumber Kloning Binary Library Copy Tercepat
4. Perbandingan kecepatan beberapa metode salinan
Saya tidak akan berbicara tentang konsep salinan dalam dalam artikel ini. Menerapkan salinan dalam di C ++. Secara umum, kelebihan muatan operator penugasan "=" untuk mengimplementasikan salinan mendalam antara objek dari kelas yang sama. Oleh karena itu, wajar jika di Java kita juga dapat menentukan fungsi salin untuk menetapkan setiap properti objek dalam fungsi. Metode ini sederhana dan alami, tetapi ada masalah fatal: Jika suatu hari atribut baru yang membutuhkan salinan dalam ditambahkan ke kelas, fungsi salinan yang sesuai juga harus dimodifikasi. Metode ini membawa ketidaknyamanan yang besar pada ekstensibilitas kelas. Cara menyelesaikan masalah ini, mari kita lihat metode implementasi Bab 1, 2, dan 3 dan uji kecepatan bagian 4.
1. Antarmuka klonasi Java mengimplementasikan salinan dalam <br /> dengan cara ini, kelas perlu mengimplementasikan fungsi klon antarmuka yang dapat dikumpulkan, dan memanggil super.clone dalam fungsi klon. Salinan yang mendalam dari metode ini juga membawa masalah lain. Jika ada objek kelas lain sebagai properti di kelas, kelas lain juga perlu kelebihan beban dan diimplementasikan di antarmuka yang dapat dikloning. Inilah contohnya. Dalam contoh berikut, Complexdo berisi objek SimpleDo. Untuk mengimplementasikan Complexdo Deep Copy, Anda harus terlebih dahulu menerapkan antarmuka klon SimpleDo:
kelas publik Simpledo mengimplementasikan kloning, serializable {private int x = 1; Private String S = "SimpleDo"; @Override Protected Object Clone () melempar clonenotsupportedException {simpledo newclass = (simpledo) super.clone (); mengembalikan newclass; }} kelas publik Complexdo mengimplementasikan kloning, serializable {private int x = 1; string pribadi s = "kompleks"; Private Integer A = 123; Private Integer B = 1234; Private Integer C = 1334455; Private String S2 = "Hehehe"; Private String S3 = "Hahaha"; Private Long ID = 1233245L; Private ArrayList <MenDLEDO> l = Daftar ArrayList baru <MenDLEDO> (); @Override Public Object Clone () melempar clonenotsupportedException {complexdo newclass = (complexdo) super.clone (); newclass.l = new ArrayList <MenDledo> (); untuk (simpledo sederhana: this.l) {newclass.l.add ((simpledo) simple.clone ()); } return newclass; }} Perlu dicatat bahwa banyak artikel mengatakan bahwa operator penugasan jenis string adalah salinan yang dalam, tetapi pada kenyataannya, mereka yang menggunakan operator penugasan di Java adalah salinan yang dangkal, tetapi mengapa artikel dengan kesalahan yang jelas seperti itu harus mengatakan bahwa ini adalah salinan yang dalam? Pemahaman saya adalah bahwa atribut string dan tipe adalah tipe dasar, dan metode yang disediakan akan objek baru selama perubahan data internal dirancang. Oleh karena itu, operasi string tidak akan mempengaruhi memori yang awalnya ditunjukkan. Oleh karena itu, secara umum, operasi penugasan kelas dasar seperti string adalah salinan yang dalam.
Untuk alasan ini, saat menggunakan splicing string string, memori baru perlu dibuka, begitu banyak orang merekomendasikan menggunakan StringBuilder alih-alih string untuk splicing, karena StringBuilder hanya menampilkan kembali memori yang lebih besar ketika rentang array char bawaan tidak cukup (untuk JVM modern, kode akan disetel, dan string string+akan dioptimalkan ke instruksi yang sama untuk StringBuilder. Untuk memangkas yang berlawanan dengan splicing, ada fungsi substring dalam string. Saat menggunakan fungsi substring, apakah array char internal dari string baru sama dengan string asli? Ini lebih menarik. Jika Anda tertarik, Anda dapat membandingkan dan memeriksa implementasi JDK1.6 dan JKD1.7.
2. Java Serialization mengimplementasikan salinan yang dalam
Prinsip metode ini adalah menggunakan serialisasi Java untuk membuat serialisasi objek ke dalam aliran byte biner, dan kemudian deserialize dan menetapkan nilai ke suatu objek. Contoh kode:
objek publik seircopy (objek src) {coba {bytearrayoutputStream byteout = new ByTeArrayOutputStream (); ObjectOutputStream out = ObjectOutputStream baru (byteout); out.writeObject (src); BytearrayInputStream bytein = bytearrayInputStream baru (byteout.tobytearray ()); ObjectInputStream in = new ObjectInputStream (bytin); Objek dest = in.readObject (); Return Dest; } catch (Exception e) {// Lakukan beberapa kesalahan pawang kembali null; }} Tentu saja, Anda juga dapat menggunakan JSON dan perpustakaan serial lainnya untuk menyelesaikan serialisasi. Metode ini secara efektif menghindari kekurangan yang dapat diperluas dari antarmuka cloneabel. Fungsi pada dasarnya dapat cocok untuk semua kelas. Kerugiannya adalah bahwa itu adalah salinan memori relatif. Serialisasi membutuhkan konversi objek terlebih dahulu menjadi aliran byte biner, dan kemudian deserializing menyalin ulang aliran byte biner ke sepotong memori objek, yang relatif lambat.
3. Analisis Kode Sumber Kloning Binary Library Copy Tercepat
Dalam kode sumber, logika pemrosesan inti ada di kelas Cloner.
Ada dua tautan rekursif:
Dalam (1), FastClone melengkapi objek yang diwarisi dari kelas antarmuka IfastClone, yaitu, semuanya adalah salinan operasi pengumpulan;
Dalam (2), CloneObject melengkapi proses mendapatkan setiap properti dari objek normal melalui mekanisme refleksi, dan kemudian menetapkan nilai ke sifat -sifat objek yang baru dihasilkan menggunakan objenesis.
Metode ini sangat dapat diperluas. Anda tidak hanya dapat mengandalkan kode yang ada untuk menyelesaikan penyalinan mendalam, tetapi Anda juga dapat menentukan beberapa metode dan jenis kloning yang tidak memerlukan kloning, yang sangat fleksibel.
4. Perbandingan kecepatan beberapa metode salinan
Tiga mode di atas dapat digunakan untuk menyelesaikan penyalinan mendalam, dan metode penyalinan tercepat adalah apa yang kita pedulikan.
Pertama, uji kode:
public void testcloneComplex () melempar clonenotsupportedException {final int copycount = 1; Daftar <Kompleksdo> complexdolist = new ArrayList <ComplexDo> (CopyCount * 3); Final ComplexDo Complex = New ComplexDo (); // Menghitung perpustakaan dua sisi Long start = System.currentTimeMillis (); untuk (int i = 0; i <copycount; ++ i) {final complexdo deepclone = clner.deepclone (kompleks); complexdolist.add (DeepClone); } long end = System.currentTimeMillis (); System.out.println ("DeepClone biaya waktu =" + (end-start)); // Memanggil fungsi klon yang diimplementasikan oleh antarmuka yang dapat dikloning start = system.currentTimeMillis (); untuk (int i = 0; i <copyCount; ++ i) {final complexdo interfaceclone = (complexdo) complex.clone (); ComplexDolist.Add (Interfaceclone); } end = system.currentTimeMillis (); System.out.println ("Antarmaceclone biaya waktu =" + (end-start)); // serialisasi dan deserialisasi menghasilkan objek baru start = system.currentTimeMillis (); untuk (int i = 0; i <copycount; ++ i) {final complexdo seirclone = seircopy (kompleks); complexdolist.add (Seirclone); } end = system.currentTimeMillis (); System.out.println ("Seirclone biaya waktu =" + (end-start)); }Unit hasil menjalankan adalah milidetik (data ini diabaikan dan tidak menghitung hotspot Java dan kemungkinan GC).
Dari tabel ini, kita dapat menarik kesimpulan:
1. Penyalin antarmuka yang dapat dikloning adalah yang tercepat, karena hanya melibatkan salinan memori, tetapi jika atribut yang terlibat adalah objek yang lebih umum, itu agak merepotkan untuk menulis.
2. Serialisasi/salinan deserialisasi adalah yang paling lambat
3. Menggunakan perpustakaan kloning, penyalinan rekursi dan mekanisme refleksi lebih lambat dari implementasi antarmuka yang dapat dikloning, tetapi lebih cepat dari metode serialisasi.
Di atas adalah semua tentang artikel ini, saya harap ini akan membantu untuk pembelajaran semua orang.