Mengapa menghasilkan serialVersionUID yang panjang alih-alih 1L sederhana?

210

Ketika kelas mengimplementasikan Serializable di Eclipse, saya memiliki dua opsi: tambahkan default serialVersionUID(1L)atau dibuat serialVersionUID(3567653491060394677L). Saya pikir yang pertama lebih keren, tetapi sering kali saya melihat orang menggunakan opsi kedua. Apakah ada alasan untuk menghasilkan long serialVersionUID?

IAdapter
sumber
49
Bagaimana duplikat yang tepat? Saya tidak bertanya mengapa menghasilkannya sama sekali, tetapi mengapa menghasilkan serialVersionUID yang panjang.
IAdapter
13
Ketika Jon Skeet menggunakan serialVersionUID, ia menggunakan 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz
8
@HannoFietz: Kalimat yang tepat adalah: "Untuk kesederhanaan saya sarankan memulai dengan 0 dan meningkatkannya dengan 1 setiap kali Anda perlu." Jadi, sepertinya dia 0Lhanya menggunakan awalnya.
ATAU Mapper
7
@ORMapper: Apakah Anda menyiratkan bahwa Jon Skeet perlu kembali dan memperbarui kode yang telah ditulisnya? Bahkan sampai pada titik ketidakcocokan struktural. Terkesiap! Bidaah!
Thilo

Jawaban:

90

Sejauh yang saya tahu, itu hanya untuk kompatibilitas dengan rilis sebelumnya. Ini hanya akan berguna jika Anda lalai menggunakan serialVersionUID sebelumnya, dan kemudian membuat perubahan yang Anda tahu harus kompatibel tetapi yang menyebabkan serialisasi rusak.

Lihat Spesifikasi Serialisasi Java untuk detail lebih lanjut.

Michael Myers
sumber
69

Tujuan dari versi serialisasi UID adalah untuk melacak berbagai versi kelas untuk melakukan serialisasi objek yang valid.

Idenya adalah untuk menghasilkan ID yang unik untuk versi tertentu dari suatu kelas, yang kemudian diubah ketika ada detail baru yang ditambahkan ke kelas, seperti bidang baru, yang akan mempengaruhi struktur objek serial.

Selalu menggunakan ID yang sama, seperti 1Lberarti bahwa di masa depan, jika definisi kelas diubah yang menyebabkan perubahan pada struktur objek berseri, akan ada kemungkinan besar masalah muncul ketika mencoba melakukan deserialisasi objek.

Jika ID dihilangkan, Java sebenarnya akan menghitung ID untuk Anda berdasarkan bidang objek, tapi saya percaya ini adalah proses yang mahal, jadi menyediakan satu secara manual akan meningkatkan kinerja.

Berikut adalah beberapa tautan ke artikel yang membahas tentang serialisasi dan versi berbagai kelas:

coobird
sumber
50
Gagasan di balik penggunaan 1L adalah agar Anda menambahnya setiap kali Anda mengubah properti atau metode kelas.
Powerlord
39
Tidak ada dampak kinerja runtime yang memungkinkan serialserionUID dihasilkan secara otomatis - itu dihasilkan pada waktu kompilasi oleh javac ... jika Anda mendekompilasi bytecode kelas, Anda benar-benar akan melihat variabel secara statis dalam bytecode.
Jared
11
Satu lagi catatan - dengan mengelola nomor secara eksplisit Anda bisa memutuskan kapan Anda menganggap versi kelas "kompatibel", daripada mengharuskan definisi kelas sama persis.
Scott Stanchfield
23
@ Jared Menurut Butir 75 di Josh Bloch's Java Efektif: Edisi ke-2: "mendeklarasikan UID versi serial eksplisit di setiap kelas serializable yang Anda tulis .... Jika tidak ada versi serial UID yang disediakan, perhitungan mahal diperlukan untuk menghasilkan satu saat runtime . "
Colin K
12
@coobird Ini sepertinya menjadi alasan utama mengapa serialVersionUID default tidak direkomendasikan Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. . Komentar di atas diambil dari Java Object Serialization Specification versi 6.0
user624558
20

Alasan utama untuk yang dihasilkan adalah untuk membuatnya kompatibel dengan versi kelas yang sudah ada yang sudah memiliki salinan.

Robin
sumber
1
OK tapi yang sama akan Jika saya selalu 1L. Semuanya akan kompatibel bahkan jika saya akan melakukan perubahan.
grep
@grep coba ganti nama bidang dan lihat apa yang terjadi.
Trejkaz
1
@grep intinya adalah bahwa jika Anda memiliki kelas yang menghapus serialVersionUID sebelumnya, itu akan mendapatkan yang dihasilkan secara otomatis. Jadi sekarang, Anda ingin mulai mengaturnya secara eksplisit, mengaturnya ke 1L akan membuatnya tidak kompatibel dengan kelas yang ada, sedangkan menggunakan nilai yang dihasilkan membuatnya tetap kompatibel.
marc82ch
15

Default "long" dari serialVersionUIDadalah nilai default seperti yang didefinisikan oleh Spesifikasi Serialisasi Java , dihitung dari perilaku serialisasi default.

Jadi, jika Anda menambahkan nomor versi default, kelas Anda (akan) membuat cerita bersambung lebih cepat asalkan tidak ada yang berubah secara struktural, tetapi Anda harus berhati-hati bahwa jika Anda mengubah kelas (menambah / menghapus bidang) Anda juga memperbarui nomor seri.

Jika Anda tidak harus kompatibel dengan bit stream yang ada, Anda bisa meletakkannya 1Ldan menambah versi sesuai kebutuhan ketika ada perubahan. Yaitu, ketika versi serialisasi standar dari kelas yang diubah akan berbeda dari versi standar dari kelas lama.

David Schmitt
sumber
11

Anda benar-benar harus membuat serialVersionUID setiap kali Anda mendefinisikan kelas yang mengimplementasikan java.io.Serializable. Jika tidak, satu akan dibuat untuk Anda secara otomatis, tetapi ini buruk. SerialVersionUID yang dibuat secara otomatis didasarkan pada tanda tangan metode kelas Anda, jadi jika Anda mengubah kelas Anda di masa depan untuk menambahkan metode (misalnya), deserializing versi "lama" kelas akan gagal. Inilah yang dapat terjadi:

  1. Buat versi pertama kelas Anda, tanpa mendefinisikan serialVersionUID.
  2. Serialisasi instance kelas Anda ke toko persisten; serialVersionUID secara otomatis dibuat untuk Anda.
  3. Ubah kelas Anda untuk menambahkan metode baru, dan gunakan kembali aplikasi Anda.
  4. Coba deserialize instance yang diserialisasi pada langkah 2, tetapi sekarang gagal (ketika itu harus berhasil), karena ia memiliki serialVersionUID yang dibuat secara otomatis.
Pankaj Kumar
sumber
1
Sebagai soal fakta, deserializing versi lama dari kelas memang harus gagal karena mereka tidak sama lagi. Anda menyarankan untuk membuat serialVersionUID sendiri untuk mencegah kegagalan (de) serialisasi ketika tanda tangan kelas berubah. Meskipun saran Anda sesuai, penjelasan Anda tentang tujuannya adalah salah dan menyesatkan. Akan bijaksana untuk mengubah jawaban Anda.
mostruash
6

Jika Anda tidak menentukan serialVersionUID maka Java membuat satu dengan cepat. SerialVersionUID yang dihasilkan adalah angka itu. Jika Anda mengubah sesuatu di kelas Anda yang tidak benar-benar membuat kelas Anda tidak kompatibel dengan vernal bersambung sebelumnya tetapi mengubah hash, maka Anda perlu menggunakan serialVersionUID nomor sangat besar yang dihasilkan (atau nomor "diharapkan" dari pesan kesalahan) . Kalau tidak, jika Anda melacak semuanya sendiri, 0, 1, 2 ... lebih baik.

joeforker
sumber
Maksud Anda ==> 1. Jika Anda ingin perubahan kelas yang berbeda agar kompatibel gunakan yang dibuat. 2. Jika Anda ingin versi kelas yang berbeda tidak kompatibel gunakan yang standar, dan berhati-hati dalam meningkatkan. Apakah saya memahaminya dengan benar?
JavaDeveloper
4

Ketika Anda menggunakan serialVersionUID (1L) daripada menghasilkan serialVersionUID (3567653491060394677L) Anda mengatakan sesuatu.

Anda mengatakan bahwa Anda 100% yakin bahwa tidak ada sistem yang akan menyentuh kelas ini yang memiliki versi serial yang tidak kompatibel dari kelas ini dengan nomor versi 1.

Jika Anda dapat memikirkan alasan apa pun untuk riwayat versi serialnya menjadi tidak dikenal, itu mungkin sulit dikatakan dengan percaya diri. Dalam masa hidupnya, kelas yang sukses akan dipertahankan oleh banyak orang, tinggal di banyak proyek, dan tinggal di banyak sistem.

Anda bisa menderita karenanya. Atau Anda bisa bermain lotre dengan harapan kalah. Jika Anda menghasilkan versi, Anda memiliki peluang kecil untuk kesalahan. Jika Anda menganggap "Hei, saya yakin belum ada yang menggunakan 1" peluang Anda lebih besar dari kecil. Justru karena kita semua berpikir 0 dan 1 itu keren sehingga Anda memiliki peluang lebih tinggi untuk memukulnya.

-

Ketika Anda menghasilkan serialVersionUID (3567653491060394677L) daripada menggunakan serialVersionUID (1L) Anda mengatakan sesuatu.

Anda mengatakan bahwa orang mungkin telah membuat atau membuat nomor versi lain secara manual selama sejarah kelas ini dan Anda tidak peduli karena Longs membuat angka besar.

Bagaimanapun, kecuali jika Anda benar-benar tahu sejarah nomor versi yang digunakan ketika membuat serial kelas di seluruh alam semesta di mana ia memiliki atau akan pernah ada, Anda mengambil risiko. Jika Anda punya waktu untuk memastikan 100% yakin 1 adalah AOK, lakukan saja. Jika itu terlalu sulit, silakan dan hasilkan secara buta. Anda lebih mungkin memenangkan lotre daripada melakukan kesalahan. Jika ya, beri tahu saya dan saya akan membelikan Anda bir.

Dengan semua pembicaraan tentang bermain lotre ini saya mungkin telah memberi Anda kesan bahwa serialVersionUID dibuat secara acak. Bahkan selama rentang angka didistribusikan secara merata untuk setiap nilai yang mungkin dari Long yang akan baik-baik saja. Namun, sebenarnya dilakukan dengan cara ini:

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

Satu-satunya perbedaan yang Anda dapatkan dengan itu adalah Anda tidak perlu sumber acak. Anda menggunakan perubahan di kelas itu sendiri untuk mengubah hasilnya. Namun menurut prinsip pigeonhole masih ada kemungkinan bisa salah dan bertabrakan. Sangat tidak mungkin. Jadi, semoga sukses membuat saya minum bir.

Namun, bahkan jika kelas hanya akan hidup dalam satu sistem dan satu basis kode, berpikir bahwa penambahan angka dengan tangan memberi Anda kesempatan nol tabrakan hanya berarti Anda tidak mengerti manusia. :)

Beberapa pria
sumber
Jika "sistem" menyentuh kelas, yaitu mengubah kelas sedemikian rupa sehingga serialisasi menjadi tidak kompatibel maka pertanyaannya adalah apakah sistem itu juga akan mengubah serialVersionUID. Saya tidak berpikir kemungkinannya lebih kecil sehingga ingat untuk mengubahnya ketika panjang. Saya pikir yang terjadi adalah kebalikannya, jika angkanya lebih mudah diingat perubahannya lebih tinggi sehingga saya perhatikan bahwa saya tidak sengaja mengubahnya.
Reto Gmür
2
Ini salah! Ketika Anda membuat serialVersionUID, dan menyatakan nilai itu dalam kode sumber Anda, daripada 1L atau tidak sama sekali Anda mengatakan: Saya ingin tabrakan yang mungkin tidak terdeteksi di masa depan dengan efek yang tidak ditentukan, dan saya tidak ingin java atau manusia mencegah ini . Jawa paranoid, tapi patuh. Manusia biasanya tidak mengacaukan jumlah yang besar. Dengan cara ini, ketika kelas berubah, java masih dapat membatalkan serialisasi versi lama yang tidak kompatibel. MwoaHaHa ...;)
Superole
1

Nah, serialVersionUID adalah pengecualian terhadap aturan bahwa "bidang statis tidak mendapatkan serial". ObjectOutputStream menulis setiap kali nilai serialVersionUID ke aliran output. ObjectInputStream membacanya kembali dan jika nilai yang dibaca dari aliran tidak setuju dengan nilai serialVersionUID dalam versi kelas saat ini, maka ia melempar InvalidClassException. Selain itu, jika tidak ada serialVersionUID yang secara resmi dideklarasikan di kelas untuk diserialisasi, kompiler secara otomatis menambahkannya dengan nilai yang dihasilkan berdasarkan bidang yang dideklarasikan di kelas.

Beruntung
sumber
0

Karena dalam banyak kasus id default tidak unik. jadi kami membuat id untuk membuat konsep unik.

Pushpendra Kuntal
sumber
Bisakah Anda mengedit jawaban Anda untuk menyempurnakannya? Ini sepertinya komentar di sini. Terima kasih.
Gray
0

Untuk menambahkan jawaban @David Schmitts, sebagai aturan praktis saya selalu menggunakan 1L default dari konvensi. Saya hanya perlu kembali dan mengubahnya beberapa kali, tetapi saya tahu bahwa ketika saya melakukan perubahan dan memperbarui nomor standar dengan satu setiap kali.

Di perusahaan saya saat ini, mereka memerlukan nomor yang dibuat secara otomatis jadi saya menggunakannya untuk konvensi, tapi saya lebih suka yang default. Pendapat saya adalah, jika ini bukan konvensi tempat Anda bekerja, gunakan default, kecuali Anda berpikir Anda akan terus mengubah struktur kelas serial Anda karena suatu alasan.

James Drinkard
sumber
0

Tujuan dari versi serialisasi UID adalah untuk melacak berbagai versi kelas untuk melakukan serialisasi objek yang valid.

Idenya adalah untuk menghasilkan ID yang unik untuk versi tertentu dari suatu kelas, yang kemudian diubah ketika ada detail baru yang ditambahkan ke kelas, seperti bidang baru, yang akan mempengaruhi struktur objek serial.

Penjelasan Sederhana:

Apakah Anda membuat serialisasi data?

Serialisasi pada dasarnya menulis data kelas ke file / stream / etc. De-serialisasi adalah membaca data itu kembali ke kelas.

Apakah Anda berniat untuk berproduksi?

Jika Anda hanya menguji sesuatu dengan data yang tidak penting / palsu, maka jangan khawatir tentang hal itu (kecuali jika Anda menguji serialisasi secara langsung).

Apakah ini versi pertama?

Jika demikian, setel serialVersionUID = 1L.

Apakah ini versi prod kedua, ketiga, dll.?

Sekarang Anda perlu khawatir tentang serialVersionUID, dan harus memeriksanya secara mendalam.

Pada dasarnya, jika Anda tidak memperbarui versi dengan benar ketika Anda memperbarui kelas yang perlu Anda tulis / baca, Anda akan mendapatkan kesalahan ketika Anda mencoba membaca data lama.

Saurabh Verma
sumber