Saya tahu pertanyaan ini benar-benar tua dan memiliki jawaban yang diterima, tetapi karena muncul sangat tinggi di pencarian google, saya pikir saya akan mempertimbangkan karena tidak ada jawaban yang mencakup tiga kasus yang saya anggap penting - dalam pikiran saya penggunaan utama untuk ini metode. Tentu saja, semua berasumsi bahwa sebenarnya ada kebutuhan untuk format serialisasi khusus.
Ambil, misalnya kelas koleksi. Serialisasi serial dari daftar tertaut atau BST akan menghasilkan hilangnya ruang yang sangat besar dengan perolehan kinerja yang sangat sedikit dibandingkan dengan hanya membuat serial elemen-elemen secara berurutan. Ini bahkan lebih benar jika koleksi adalah proyeksi atau tampilan - menyimpan referensi ke struktur yang lebih besar daripada yang diekspos oleh API publiknya.
Jika objek berseri memiliki bidang yang tidak dapat diubah yang membutuhkan serialisasi khusus, solusi asli writeObject/readObject
tidak mencukupi, karena objek deserialisasi dibuat sebelum membaca bagian aliran yang ditulis writeObject
. Ambil penerapan minimal dari daftar tertaut ini:
public class List<E> extends Serializable {
public final E head;
public final List<E> tail;
public List(E head, List<E> tail) {
if (head==null)
throw new IllegalArgumentException("null as a list element");
this.head = head;
this.tail = tail;
}
//methods follow...
}
Struktur ini dapat diserialisasi dengan menulis head
bidang setiap tautan secara rekursif , diikuti oleh sebuah null
nilai. Namun deserialisasi format seperti itu menjadi tidak mungkin: readObject
tidak dapat mengubah nilai-nilai bidang anggota (sekarang diperbaiki ke null
). Berikut datang writeReplace
/ readResolve
pair:
private Object writeReplace() {
return new Serializable() {
private transient List<E> contents = List.this;
private void writeObject(ObjectOutputStream oos) {
List<E> list = contents;
while (list!=null) {
oos.writeObject(list.head);
list = list.tail;
}
oos.writeObject(null);
}
private void readObject(ObjectInputStream ois) {
List<E> tail = null;
E head = ois.readObject();
if (head!=null) {
readObject(ois); //read the tail and assign it to this.contents
this.contents = new List<>(head, this.contents)
}
}
private Object readResolve() {
return this.contents;
}
}
}
Saya minta maaf jika contoh di atas tidak mengkompilasi (atau bekerja), tetapi mudah-mudahan cukup untuk menggambarkan poin saya. Jika menurut Anda ini adalah contoh yang sangat jauh, harap diingat bahwa banyak bahasa fungsional dijalankan pada JVM dan pendekatan ini menjadi sangat penting dalam kasus mereka.
Kita mungkin ingin melakukan deserialisasi objek dari kelas yang berbeda dari yang kita tulis ke ObjectOutputStream
. Ini akan menjadi kasus dengan pandangan seperti java.util.List
implementasi daftar yang memperlihatkan sepotong lebih lama ArrayList
. Jelas, membuat serial seluruh daftar dukungan adalah ide yang buruk dan kita hanya harus menulis elemen dari slice yang dilihat. Tetapi mengapa berhenti pada itu dan memiliki tingkat tipuan yang tidak berguna setelah deserialisasi? Kami hanya bisa membaca elemen dari aliran ke dalam ArrayList
dan mengembalikannya secara langsung alih-alih membungkusnya di kelas tampilan kami.
Atau, memiliki kelas delegasi serupa yang didedikasikan untuk serialisasi dapat menjadi pilihan desain. Contoh yang baik akan menggunakan kembali kode serialisasi kami. Sebagai contoh, jika kita memiliki kelas builder (mirip dengan StringBuilder untuk String), kita dapat menulis delegasi serialisasi yang membuat serial setiap koleksi dengan menulis builder kosong ke stream, diikuti dengan ukuran koleksi dan elemen yang dikembalikan oleh iterator colection. Deserialisasi akan melibatkan pembacaan pembangun, menambahkan semua elemen yang selanjutnya dibaca, dan mengembalikan hasil final build()
dari delegasireadResolve
. Dalam hal ini kita perlu mengimplementasikan serialisasi hanya di kelas akar hirarki koleksi, dan tidak ada kode tambahan yang diperlukan dari implementasi saat ini atau di masa depan, asalkan mereka menerapkan abstrak iterator()
danbuilder()
metode (yang terakhir untuk membuat ulang koleksi dari jenis yang sama - yang akan menjadi fitur yang sangat berguna dalam dirinya sendiri). Contoh lain adalah memiliki hierarki kelas yang kode kita tidak sepenuhnya kendalikan - kelas dasar kita dari pustaka pihak ketiga dapat memiliki sejumlah bidang pribadi yang tidak kita ketahui dan yang dapat berubah dari satu versi ke versi lain, melanggar objek serial kami. Dalam hal ini akan lebih aman untuk menulis data dan membangun kembali objek secara manual pada deserialisasi.
String.CaseInsensitiveComparator.readResolve()