Mengapa Java membutuhkan antarmuka Serializable?

114

Kami bekerja keras dengan serialisasi dan harus menentukan tag Serializable pada setiap objek yang kami gunakan adalah semacam beban. Terutama saat itu adalah kelas pihak ke-3 yang tidak bisa kita ubah.

Pertanyaannya adalah: karena Serializable adalah antarmuka kosong dan Java menyediakan serialisasi yang kuat setelah Anda menambahkan implements Serializable- mengapa mereka tidak membuat semuanya dapat diserialkan dan hanya itu?

Apa yang saya lewatkan?

Yoni Roit
sumber
Bagaimana jika Anda ingin membuat objek Anda sendiri dapat diserialkan? Atau apakah saya salah paham?
Joe Phillips
Saya masih akan mendapatkan NotSerializableException karena semua bidang objek saya harus dapat diserialkan
Yoni Roit
Sepenuhnya setuju dengan Pop Catalin dan dmitry "Trik Serializable ini hanyalah salah satu keputusan yang salah yang telah diambil satu atau dua dekade lalu". Tidak peduli betapa berbahayanya serialisasi. Dan tidak benar bahwa karena deklarasi bersifat eksplisit maka "Anda tahu bahwa Anda harus memberi perhatian khusus": setiap orang yang membutuhkannya pertama-tama meletakkan barang "mengimplementasikan", dan kemudian memikirkan implikasinya jika terjadi kesalahan. Semuanya bisa lebih jelas jika mereka memberi kami antarmuka "Unserializable" untuk diterapkan pada kasus khusus.
Jack

Jawaban:

120

Serialisasi penuh dengan jebakan. Dukungan serialisasi otomatis dari formulir ini membuat kelas internal menjadi bagian dari API publik (itulah sebabnya javadoc memberi Anda bentuk kelas yang bertahan ).

Untuk ketekunan jangka panjang, kelas harus dapat memecahkan kode formulir ini, yang membatasi perubahan yang dapat Anda buat pada desain kelas. Ini merusak enkapsulasi.

Serialisasi juga dapat menyebabkan masalah keamanan. Dengan dapat membuat serialisasi objek apa pun yang dirujuknya, kelas dapat mengakses data yang biasanya tidak dapat diaksesnya (dengan mengurai data byte yang dihasilkan).

Ada masalah lain, seperti bentuk serial dari kelas dalam yang tidak didefinisikan dengan baik.

Membuat semua kelas dapat diserialkan akan memperburuk masalah ini. Lihat Java Edisi Kedua yang Efektif , khususnya Item 74: Implementasikan Serializable dengan bijaksana .

McDowell
sumber
2
Ini jelas merupakan jawaban yang lebih baik, saya kecewa karena jawaban yang dipilih bukan ini, tampaknya yang diposting memilih dengan agenda "Saya kesal karena saya harus menyatakan hal-hal yang dapat diserialkan" dalam pikiran. Ini adalah contoh seseorang yang ingin membebaskan diri dan tidak mengindahkan pelajaran keamanan dan desain yang telah dipelajari di masa lalu.
gbtimmon
@McDowell apa yang Anda maksud dengan bentuk kelas yang bertahan? Saya mengikuti tautan itu tetapi tidak mengerti apa yang Anda maksud? bisakah Anda menjelaskan ini?
Geek
@Geek - Bentuk serial dari jenis URL (misalnya) menentukan bidang pribadi apa yang harus dimiliki jenis dan urutannya harus dideklarasikan.
McDowell
@McDowell Mengapa urutan penting?
Geek
12
@Sukasukaa_hati. Saya adalah pembuat asli pertanyaan ini dari 4 tahun yang lalu, dan saya baru saja memilih jawaban Anda sebagai diterima, jika itu berarti. Saya pikir jawaban Anda benar-benar lebih baik, dan saya mungkin terlalu tidak dewasa untuk melihatnya seperti itu pada saat itu. Memperbaiki sekarang :)
Yoni Roit
32

Saya pikir orang-orang Java dan .Net salah kali ini, akan lebih baik untuk membuat semuanya dapat diserialkan secara default dan hanya perlu menandai kelas-kelas yang tidak dapat diserialkan dengan aman.

Misalnya di Smalltalk (bahasa yang dibuat tahun 70-an) setiap objek dapat diserialkan secara default. Saya tidak tahu mengapa ini tidak terjadi di Java, mengingat fakta bahwa sebagian besar objek aman untuk diserialkan dan hanya beberapa yang tidak.

Menandai objek sebagai dapat diserialkan (dengan antarmuka) tidak secara ajaib membuat objek itu dapat diserialkan, itu dapat diserialkan selama ini , hanya saja sekarang Anda mengungkapkan sesuatu yang dapat ditemukan sistem sendiri, jadi saya tidak melihat alasan yang benar-benar bagus untuk serialisasi menjadi seperti sekarang.

Saya pikir itu adalah keputusan buruk yang dibuat oleh desainer atau serialisasi adalah renungan, atau platform tidak pernah siap untuk melakukan serialisasi secara default pada semua objek dengan aman dan konsisten.

Pop Catalin
sumber
26
Anda perlu berhati-hati saat mendesain dan mengimplementasikan kelas untuk memastikan bahwa instance akan diserialkan dengan cara yang masuk akal. Antarmuka yang dapat diserialkan sebenarnya berarti: "Saya, sebagai programmer, telah memahami konsekuensi serialisasi dan mengizinkan JVM untuk membuat serial ini"
Rolf Rander
@ Rolf Rander: sering kali Anda tidak perlu berhati-hati sama sekali, cukup tandai kelas yang dapat diserialkan. Jika serialisasi AKTIF secara defaul pada semua objek, pola pikir setiap pengembang juga akan berbeda, akan menjadi sesuatu yang wajar untuk dilakukan untuk membuat kelas dapat diserialkan ...
Pop Catalin
itu akan menambah perhatian lain ke daftar desain kelas yang baik, seperti misalnya memastikan Anda tidak memiliki kebocoran memori. Namun, desain kelas yang baik semakin banyak mengharuskan kelas Anda dapat diserialkan ketika mereka bisa.
Pop Catalin
3
@StaxMan, karena menyadari nanti bahwa Anda perlu membuat serial kelas dan Anda tidak bisa, bisa sangat mahal. Ini salah satu kasus di mana sedikit usaha ekstra terbayar. Ini terutama benar jika Anda sedang menulis perpustakaan dan orang lain akan mengkonsumsinya tanpa kode sumber
Pop Catalin
2
Saya sangat setuju dengan jawaban ini. Dalam banyak bahasa semuanya dapat diserialkan secara default. Dan sebenarnya sama dengan JVM, karena mudah digunakan Reflection untuk mengakses setiap anggota kelas, baik itu privat maupun tidak. Ini Serializabletrik adalah salah satu keputusan lain yang salah yang telah diambil satu atau dua dekade yang lalu, dan satu lagi Selain jengkel ketika berhadapan dengan java murni, seperti beberapa kelemahan dalam pengumpulan dan tali pengolahan di perpustakaan standar. Syukurlah ada Kryo, tapi itu ketergantungan dan kita perlu menemukannya terlebih dahulu. Beginilah seharusnya serialisasi built-in dilakukan.
dmitry
20

Tidak semuanya benar-benar dapat serial. Ambil koneksi soket jaringan, misalnya. Anda dapat membuat serialisasi data / status objek soket Anda, tetapi inti dari koneksi aktif akan hilang.

Joel Coehoorn
sumber
Ini akan menjadi masalah saya dan jika saya tidak cukup pintar untuk mencoba membuat serialisasi soket, saya akan menemukan kesalahan saya selama debugging. Namun, sekarang saya dalam situasi ketika saya tidak dapat menggunakan serialisasi Java sama sekali karena kelas pihak ke-3 yang tidak mengimplementasikan Serializable tanpa alasan yang baik.
Yoni Roit
4
Ada beberapa cara yang layak untuk menangani kasus ini, seperti menulis kelas pembungkus Serializable yang tahu cara membaca & menulis data kunci dari kelas pihak ke-3; buat instance yang dibungkus menjadi sementara dan ganti writeObject dan readObject.
Greg Case
Bisakah Anda mewarisi dari objek yang dibutuhkan dalam api Anda dan membuat serial kelas-kelas itu?
Joel Coehoorn
@ Joel: Itu ide yang bagus, tapi tetap saja hack. Saya kira semua ini hanyalah pengorbanan yang salah. Terima kasih atas komentar Anda.
Yoni Roit
1
Ini adalah salah satu contoh langka yang menggambarkan bahwa yang kita butuhkan hanyalah implements NotSerializable:)
Rob Grant
13

Peran utama Serializable di Java adalah untuk benar-benar membuat, secara default, semua objek lain tidak dapat dialirkan. Serialisasi adalah mekanisme yang sangat berbahaya, terutama dalam implementasi defaultnya. Karenanya, seperti persahabatan di C ++, ini dinonaktifkan secara default, meskipun biayanya sedikit untuk membuatnya dapat diserialkan.

Serialisasi menambah kendala dan potensi masalah karena kompatibilitas struktur tidak diasuransikan. Itu bagus karena dimatikan secara default.

Saya harus mengakui bahwa saya telah melihat sangat sedikit kelas nontrivial di mana serialisasi standar melakukan apa yang saya inginkan. Terutama dalam kasus struktur data yang kompleks. Jadi upaya yang Anda habiskan untuk membuat kelas dapat berseri dengan benar mengurangi biaya penambahan antarmuka.

Uri
sumber
9

Untuk beberapa kelas, terutama yang mewakili sesuatu yang lebih fisik seperti File, Socket, Thread, atau koneksi DB, sama sekali tidak masuk akal untuk membuat serial instans. Bagi banyak orang lain, Serialisasi mungkin bermasalah karena menghancurkan batasan keunikan atau hanya memaksa Anda untuk menangani contoh versi berbeda dari kelas, yang mungkin tidak Anda inginkan.

Bisa dibilang, mungkin lebih baik untuk membuat semuanya Serializable secara default dan membuat kelas non-serializable melalui antarmuka kata kunci atau penanda - tapi kemudian, mereka yang harus menggunakan opsi itu mungkin tidak akan memikirkannya. Begitulah, jika Anda perlu mengimplementasikan Serializable, Anda akan diberi tahu oleh Exception.

Michael Borgwardt
sumber
4

Saya pikir pemikiran itu adalah untuk memastikan Anda, sebagai programmer, tahu bahwa objek Anda adalah serialisasi.

Milhous
sumber
1

Harus menyatakan secara eksplisit bahwa instance dari kelas tertentu dapat berseri, bahasa memaksa Anda untuk memikirkan apakah Anda harus mengizinkannya. Untuk serialisasi objek nilai sederhana itu sepele, tetapi dalam kasus yang lebih kompleks Anda harus benar-benar memikirkan semuanya.

Dengan hanya mengandalkan dukungan serialisasi standar JVM, Anda dapat menghadapi semua jenis masalah pembuatan versi yang tidak menyenangkan.

Keunikan, referensi ke sumber daya 'nyata', pengatur waktu, dan banyak jenis artefak BUKAN untuk serialisasi.

Jeroen van Bergen
sumber
0

Jawaban saya adalah ini bukan alasan yang bagus. Dan dari komentar Anda, saya dapat melihat bahwa Anda telah mempelajarinya. Bahasa lain dengan senang hati mencoba membuat serialisasi segala sesuatu yang tidak melompat pada pohon setelah Anda menghitung sampai 10. Sebuah Objek harus default menjadi serializable.

Jadi, yang pada dasarnya perlu Anda lakukan adalah membaca semua properti kelas pihak ketiga Anda sendiri. Atau, jika itu adalah pilihan untuk Anda: dekompilasi, masukkan kata kunci sialan itu di sana, dan kompilasi ulang.

nes1983
sumber
2
Serialisasi menambah kendala dan potensi masalah karena kompatibilitas struktur tidak diasuransikan. IMHO, Itu bagus karena dimatikan secara default.
Uri
Saya tidak yakin apa yang Anda maksud dengan "kompatibilitas struktur".
nes1983
0

Ada beberapa hal di Java yang tidak dapat diserialkan karena bersifat spesifik waktu proses. Hal-hal seperti aliran, utas, runtime, dll. Dan bahkan beberapa kelas GUI (yang terhubung ke OS yang mendasarinya) tidak dapat diserialkan.


sumber
0

Sementara saya setuju dengan poin yang dibuat dalam jawaban lain di sini, masalah sebenarnya adalah dengan deserialisation: Jika definisi kelas berubah maka ada risiko nyata deserialisation tidak akan berhasil. Tidak pernah mengubah bidang yang ada adalah komitmen utama yang harus dibuat oleh penulis perpustakaan! Mempertahankan kompatibilitas API sudah cukup menjadi tugas yang berat.

CurtainDog
sumber
Ini tidak benar. Anda perlu membaca bab Versioning of Serializable Objects dari Object Serialization Specification, yang tidak akan ada jika apa yang Anda klaim di sini benar. Definisi kelas dapat berubah dalam batas yang agak lebar sebelum menjadi tidak kompatibel dengan serialisasi sebelumnya. Dan tentu saja itu bukanlah jawaban atas pertanyaan itu. Alasan sebenarnya berkaitan dengan masalah keamanan.
Marquis dari Lorne
@EJP, untuk kepentingan kejujuran, saya telah mengedit jawaban saya untuk mencerminkan poin Anda. Sentimen tetap ada, ini adalah komitmen besar yang harus dipilih daripada memilih keluar
CurtainDog
0

Kelas yang perlu dipertahankan ke file atau media lain harus mengimplementasikan antarmuka yang dapat diserialisasi, sehingga JVM dapat mengizinkan objek kelas untuk diserialkan. Mengapa kelas Objek tidak serialisasi maka tidak ada kelas yang perlu mengimplementasikan antarmuka, setelah semua JVM membuat serial kelas hanya ketika saya menggunakan ObjectOutputStream yang berarti kontrol masih di tangan saya untuk membiarkan JVM untuk membuat serial.

Alasan mengapa kelas Objek tidak dapat diserialkan secara default dalam kenyataan bahwa versi kelas adalah masalah utama. Oleh karena itu, setiap kelas yang tertarik dengan serialisasi harus ditandai sebagai Serializable secara eksplisit dan memberikan nomor versi serialVersionUID.

Jika serialVersionUID tidak tersedia, kami mendapatkan hasil yang tidak diharapkan saat melakukan deserialisasi objek, itulah sebabnya JVM menampilkan InvalidClassException jika serialVersionUID tidak cocok. Oleh karena itu setiap kelas harus mengimplementasikan antarmuka Serializable dan menyediakan serialVersionUID untuk memastikan Kelas yang disajikan di kedua ujungnya identik.

Trinesh Andhurthi
sumber