Neden basit bir 1L yerine uzun serialVersionUID oluşturmalı?


210

Sınıf Eclipse içinde Serializable uyguladığında, iki seçeneğim var: varsayılan serialVersionUID(1L)veya oluşturulan ekleyin serialVersionUID(3567653491060394677L). Birincisinin daha havalı olduğunu düşünüyorum, ancak birçok kez ikinci seçeneği kullanan insanları gördüm. Üretmek için herhangi bir neden var mı long serialVersionUID?


49
Bu nasıl aynısı? Neden hiç oluşturduğunu sormuyorum, ama neden uzun serialVersionUID üretiyoruz.
IAdapter

13
Jon Skeet serialVersionUID kullandığında, 0L kullanır: stackoverflow.com/questions/605828/… ;)
Hanno Fietz

8
@HannoFietz: Kesin cümle şöyledir: "Basitlik için 0 ile başlamayı ve her ihtiyacınız olduğunda 1 oranında artırmayı öneririm ." Yani, 0Lsadece başlangıçta kullandığı anlaşılıyor .
VEYA Haritacı

7
@ ORMapper: Jon Skeet'in geri dönüp yazdığı kodu güncellemesi gerektiğini ima ediyor musunuz? Hatta yapısal uyumsuzluk noktasına kadar. Gasp! Sapkınlık!
Thilo

Yanıtlar:


90

Anlayabildiğim kadarıyla, bu sadece önceki sürümlerle uyumluluk için olurdu. Bu yalnızca daha önce bir serialVersionUID kullanmayı ihmal ettiyseniz ve daha sonra uyumlu olması gerektiğini bildiğiniz ancak serileştirmenin bozulmasına neden olan bir değişiklik yaptıysanız yararlı olacaktır .

Daha fazla bilgi için Java Serialization Spec'e bakın.


69

Serileştirme sürümü UID'sinin amacı, nesnelerin geçerli serileştirmesini gerçekleştirmek için bir sınıfın farklı sürümlerini takip etmektir.

Fikir, bir sınıfın belirli bir sürümüne özgü olan bir kimlik oluşturmaktır; bu, sınıfa eklenen yeni ayrıntılar (örneğin, serileştirilmiş nesnenin yapısını etkileyecek olan) eklendiğinde değiştirilir.

Her zaman aynı kimliği kullanmak, örneğin 1Lgelecekte, serileştirilmiş nesnenin yapısında değişikliklere neden olan sınıf tanımı değiştirilirse, bir nesnenin serisini kaldırmaya çalışırken sorunların ortaya çıkma ihtimali yüksektir.

Kimlik atlanırsa, Java aslında nesnenin alanlarına göre kimliği sizin için hesaplayacaktır, ancak bunun pahalı bir süreç olduğuna inanıyorum, bu yüzden manuel olarak bir tane sağlamak performansı artıracaktır.

İşte serileştirmeyi ve sınıfların versiyonunu tartışan makalelere birkaç bağlantı:


50
1L kullanmanın arkasındaki fikir, sınıf özelliklerini veya yöntemlerini her değiştirdiğinizde artırmanızdır.
Powerlord

39
SerialversionUID öğesinin otomatik olarak oluşturulmasına izin vermenin bir çalışma zamanı performansı etkisi yoktur - javac tarafından derleme zamanında oluşturulur ... sınıfın bayt kodunu kodalıyorsanız, değişkeni aslında bayt kodunda statik olarak görürsünüz.
Jared

11
Bir not daha - sayıyı açıkça yöneterek, sınıf tanımının tamamen aynı olmasını istemek yerine, bir sınıfın sürümlerini ne zaman "uyumlu" olarak değerlendirdiğinize karar vereceksiniz.
Scott Stanchfield

23
@ Jared Josh Bloch'un Etkili Java'sında 75. Maddeye göre: 2. Baskı: "yazdığınız her serileştirilebilir sınıfta açık bir seri versiyon UID bildirir .... Seri versiyon UID sağlanmazsa, çalışma zamanında bir tane oluşturmak için pahalı bir hesaplama gerekir ."
Colin K

12
@coobird Bu, varsayılan serialVersionUID'nin önerilmemesinin ana nedeni gibi görünüyor 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. Yukarıdaki yorum, Java Nesne
Dizileştirme

20

Oluşturulan olanın ana nedeni, onu sınıfın zaten kopyaları var olan mevcut bir sürümüyle uyumlu hale getirmektir.


1
Tamam ama aynı olacak Ben her zaman 1L varsa. Herhangi bir değişiklik yapsam bile her şey uyumlu olacak.
grep

@grep bir alanı yeniden adlandırmayı deneyin ve sonra ne olduğunu görün.
Trejkaz

1
@grep, daha önce serialVersionUID değerini atlayan bir sınıfınız varsa, oluşturulan sınıfı otomatik olarak almış olacağı yönündedir. Şimdi, bunu açıkça ayarlamaya başlamak istiyorsunuz, 1L olarak ayarlamak mevcut sınıfla uyumsuz hale gelirken, üretilen değeri kullanmak onu uyumlu tutar.
marc82ch

15

"Uzun" varsayılanı , varsayılan serileştirme davranışından hesaplanan Java Serileştirme BelirtimiserialVersionUID tarafından tanımlanan varsayılan değerdir .

Bu nedenle, varsayılan sürüm numarasını eklerseniz, hiçbir şey yapısal olarak değişmediği sürece sınıfınız daha hızlı serileştirilir (de-), ancak sınıfı değiştirirseniz (alan ekle / kaldır) seri numarası.

Mevcut bit akışlarıyla uyumlu olmanız 1Lgerekmiyorsa, bir şey değiştiğinde oraya yerleştirebilir ve sürümü gerektiği gibi artırabilirsiniz . Yani, değiştirilen sınıfın varsayılan serileştirme sürümü eski sınıfın varsayılan sürümünden farklı olduğunda.


11

Uygulayan her sınıfı tanımladığınızda kesinlikle bir serialVersionUID oluşturmanız gerekir java.io.Serializable. Bunu yapmazsanız, sizin için otomatik olarak bir tane oluşturulur, ancak bu kötüdür. Otomatik olarak oluşturulan serialVersionUID, sınıfınızın yöntem imzalarına dayanır, bu nedenle gelecekte bir yöntem eklemek için sınıfınızı değiştirirseniz (örneğin), sınıfın "eski" sürümlerinin serileştirilmesi başarısız olur. İşte ne olabilir:

  1. SerialVersionUID öğesini tanımlamadan sınıfınızın ilk sürümünü oluşturun.
  2. Sınıfınızın bir örneğini kalıcı bir mağazaya seri haline getirin; sizin için otomatik olarak bir serialVersionUID oluşturulur.
  3. Yeni bir yöntem eklemek için sınıfınızı değiştirin ve uygulamanızı yeniden konuşlandırın.
  4. 2. adımda serileştirilen örneğin serisini kaldırmayı deneyin, ancak şimdi (başarılı olması gerektiğinde) başarısız olur, çünkü farklı bir otomatik olarak oluşturulmuş serialVersionUID'ye sahiptir.

1
Nitekim, sınıfın eski sürümlerinin serileştirilmesi, artık aynı olmadıkları için gerçekten başarısız olmalıdır. Sınıf imzası değiştiğinde seri hale getirme hatalarını (de) önlemek için kendi başına serialVersionUID oluşturmanızı öneririz. Öneriniz uygun olsa da, amacına ilişkin açıklamanız yanlış ve yanıltıcıdır. Cevabınızı değiştirmek akıllıca olacaktır.
mostruash

6

Bir serialVersionUID belirtmezseniz, Java anında bir tane yapar. Oluşturulan serialVersionUID bu sayıdır. Sınıfınızda sınıfınızı önceki serileştirilmiş sürümlerle gerçekten uyumlu hale getirmeyen ancak karmayı değiştiren bir şeyi değiştirirseniz, oluşturulan çok büyük numaralı serialVersionUID'yi (veya hata mesajından "beklenen" sayıyı) kullanmanız gerekir. . Aksi takdirde, her şeyi kendiniz takip ediyorsanız, 0, 1, 2 ... daha iyidir.


==> 1 demek istediniz. Farklı sınıf değişikliklerinin uyumlu olmasını istiyorsanız, oluşturulan sınıfı kullanın. 2. Sınıfların farklı sürümlerinin uyumsuz olmasını istiyorsanız, varsayılan olanı kullanın ve arttırmada ihtiyatlı olun. Doğru anladım mı?
JavaDeveloper

4

SerialVersionUID (3567653491060394677L) oluşturmak yerine serialVersionUID (1L) kullandığınızda bir şey söylüyorsunuz.

Bu sınıfın uyumsuz bir serileştirilmiş sürümüne sahip 1'in sürüm numarasına sahip bu sınıfa dokunmayacak hiçbir sistemin% 100 olduğundan eminsiniz.

Serileştirilmiş sürüm geçmişinin bilinmemesi için herhangi bir mazeret düşünürseniz, bunu güvenle söylemek zor olabilir. Yaşamı boyunca başarılı bir sınıf birçok kişi tarafından sürdürülecek, birçok projede yaşayacak ve birçok sistemde ikamet edecek.

Bunun üzerinde acı çekebilirsiniz. Ya da kaybetmeyi umarak piyango oynayabilirsiniz. Sürümü oluşturursanız, bazı şeylerin yanlış gitme şansınız vardır. Eğer "Hey henüz hiç kimse 1 kullanmadım" varsa, şansınız çok küçüktür. Kesinlikle hepimiz 0 ve 1'in onları vurma olasılığınızın yüksek olduğunu düşündüğümüz için düşünüyoruz.

-

SerialVersionUID (1L) kullanmak yerine serialVersionUID (3567653491060394677L) oluşturduğunuzda bir şey söylüyorsunuz.

İnsanların bu sınıfın geçmişi boyunca manuel olarak başka sürüm numaraları oluşturduğunu veya oluşturduğunu ve umursamadığınızdan dolayı Longs büyük sayıları korkutuyor.

Her iki durumda da, sınıfın bulunduğu ya da var olacağı tüm evrende serileştirilirken kullanılan sürüm numaralarının geçmişini mükemmel bir şekilde bilmiyorsanız, bir şans elde edersiniz. 100'ün 1'in AOK olduğundan emin olmak için zamanınız varsa, gidin. Bu çok işe yarıyorsa, devam edin ve körü körüne sayı oluşturun. Piyangoyu kazanma ihtimaliniz yanlıştır. Varsa, bana bildirin, size bir bira alacağım.

Piyango oynamanın tüm bu konuşmalarıyla size serialVersionUID'nin rastgele oluşturulduğu izlenimini vermiş olabilirim. Aslında, sayı aralığı, uzun olan her olası değere eşit olarak dağıtıldığı sürece. Ancak, aslında şu şekilde yapılır:

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

Bununla elde ettiğiniz tek fark, rastgele bir kaynağa ihtiyacınız olmamasıdır. Sonucu değiştirmek için sınıftaki değişiklikleri kullanıyorsunuz. Ancak güvercin deliği prensibine göre hala yanlış gitme ve çarpışma olasılığı vardır. Bu inanılmaz derecede olası değil. Benden bir bira almak iyi şanslar.

Bununla birlikte, sınıf sadece bir sistemde ve bir kod tabanında yaşayacak olsa bile, sayıyı elle arttırmanın size sıfır çarpışma şansı verdiğini düşünmek, sadece insanları anlamadığınız anlamına gelir. :)


Eğer bir "sistem" sınıfa dokunursa, yani sınıfı serileştirmenin uyumsuz hale gelecek şekilde değiştirirse, o zaman bu sistemin serialVersionUID değerini de değiştirip değiştirmeyeceği sorusudur. Oranların daha uzun olduğunu değiştirmeyi hatırlayacağını düşünmüyorum. Bence bunun tam tersi doğrudur, eğer sayıları hatırlamak daha kolaysa değişiklikler daha yüksekse, yanlışlıkla değiştirmediğimi fark ettim.
Reto Gmür

2
Bu yanlış! SerialVersionUID oluşturduğunuzda ve kaynak kodunuzda bu değeri 1L veya gerçekte söylediğiniz hiçbir şey yerine bildirdiğinizde: Gelecekte tanımlanmamış efektlerle muhtemelen algılanamayan bir çarpışma istiyorum ve bunu önlemek için java veya herhangi bir insanın olmasını istemiyorum . Java paranoyaktır, ancak itaatkardır. İnsanlar genellikle büyük sayılarla uğraşmazlar. Bu şekilde, sınıf değiştiğinde, java hala eski uyumsuz sürümlerinin serisini kaldırabilir. MwoaHaHa ...;)
Superole

1

SerialVersionUID, “statik alanların serileştirilmediği” kuralının bir istisnasıdır. ObjectOutputStream, serialVersionUID değerinin değerini her zaman çıkış akışına yazar. ObjectInputStream bunu geri okur ve akıştan okunan değer, sınıfın geçerli sürümündeki serialVersionUID değeriyle uyuşmuyorsa, InvalidClassException özel durumunu atar. Ayrıca, sınıfta serileştirileceği resmi olarak bildirilen bir serialVersionUID yoksa, derleyici bunu sınıfta bildirilen alanlara göre oluşturulan bir değerle otomatik olarak ekler.


0

Çoğu durumda varsayılan kimlik benzersiz değildir. bu yüzden benzersiz bir konsept yapmak için id oluşturuyoruz.


Daha fazla sonuç almak için cevabınızı düzenleyebilir misiniz? Bu bir yorum gibi görünüyor. Teşekkürler.
Gri

0

@David Schmitts cevabına eklemek için, genel bir kural olarak her zaman geleneksel 1L konvansiyonu kullanmazdım. Sadece birkaç kez geri dönüp bazılarını değiştirmek zorunda kaldım, ama değişikliği yaptığımda ve varsayılan sayıyı her seferinde bir kez güncellediğimi biliyordum.

Mevcut şirketimde otomatik olarak oluşturulan numaraya ihtiyaç duyuyorlar, bu yüzden bunu kongre için kullanıyorum, ancak varsayılanı tercih ediyorum. Benim görevim, eğer çalıştığınız bir kongre değilse, herhangi bir nedenle serileştirilmiş sınıflarınızın yapısını sürekli olarak değiştireceğinizi düşünmüyorsanız, varsayılanı kullanın.


0

Serileştirme sürümü UID'sinin amacı, nesnelerin geçerli serileştirmesini gerçekleştirmek için bir sınıfın farklı sürümlerini takip etmektir.

Fikir, bir sınıfın belirli bir sürümüne özgü olan bir kimlik oluşturmaktır; bu, sınıfa eklenen yeni ayrıntılar (örneğin, serileştirilmiş nesnenin yapısını etkileyecek olan) eklendiğinde değiştirilir.

Basit Bir Açıklama:

Verileri serileştiriyor musunuz?

Serileştirme temel olarak sınıf verilerini bir dosyaya / akışa / vb. Diziselleştirme, bu verileri bir sınıfa geri okuyor.

Üretime geçmeyi düşünüyor musunuz?

Sadece önemsiz / sahte verilerle bir şey test ediyorsanız, endişelenmeyin (doğrudan serileştirmeyi test etmiyorsanız).

Bu ilk versiyon mu?

Öyleyse serialVersionUID = 1L ayarını yapın.

Bu ikinci, üçüncü, vb eşya versiyonu mu?

Şimdi serialVersionUID hakkında endişelenmeniz ve derinlemesine bakmanız gerekiyor.

Temel olarak, yazmanız / okumanız gereken bir sınıfı güncellediğinizde sürümü doğru şekilde güncellemezseniz, eski verileri okumaya çalıştığınızda bir hata alırsınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.