Bir dahaki sefere öldürüldüğümde, serialVersionUID'yi gelişigüzel değiştirmeye cesaret edemeyeceğim

Serileştirme, nesnenin kalıcılığının bir aracıdır. Ağ iletimi ve RMI gibi senaryolarda yaygın olarak kullanılır. Sınıf, serileştirme işlevini etkinleştirmek için java.io.Serializable arabirimini uygular.

Ancak, tanıtılmamış ve serialVersionUID ile ilgili başka bir bilgi noktası daha vardır. Bu alanın kullanımı nedir? Ayarlamazsanız ne olur? Neden "Alibaba Java Geliştirme Kılavuzu" nda aşağıdaki hükümler yer almaktadır:

arkaplan bilgisi

Bu makalenin girişine başlamadan önce, serileştirme ile ilgili bazı bilgileri kısaca tanıtalım.

Serileştirilebilir ve Haricileştirilebilir

Java sınıfı, serileştirme işlevini etkinleştirmek için java.io.Serializable arabirimini uygular. Bu arabirimi uygulamayan sınıflar serileştirilemez veya serileştirilemez. Serileştirilebilir bir sınıfın tüm alt türleri kendi başlarına serileştirilebilir.

Okuyucular Serializable'ın kaynak kodunu okuduysa, onun içinde hiçbir şey olmayan boş bir arayüz olduğunu göreceklerdir. Serileştirilebilir arabirimin yöntemi veya alanı yoktur ve yalnızca serileştirilebilir semantiği tanımlamak için kullanılır. Ancak, bir sınıf bu arabirimi uygulamazsa ve serileştirilmek isterse, bir java.io.NotSerializableException atılır.

Yalnızca arabirimi uygulayan yöntemlerin serileştirilebilmesini ve serileştirilmesinin kaldırılmasını nasıl sağlar?

Bunun nedeni, serileştirme işlemi sırasında aşağıdaki kodun çalıştırılacak olmasıdır:

Serileştirme işlemi sırasında, serileştirilecek sınıfın Enum, Array ve Serializable türlerinden olup olmadığını belirleyecek ve hiçbiri yoksa NotSerializableException doğrudan atılacaktır.

Haricileştirilebilir arabirim, serileştirme yetenekleri sağlamak için de uygulanabilen Java'da sağlanmıştır.

Serializable'dan haricileştirilebilir kalıtımlar ve bu arayüzde iki soyut yöntem tanımlanmıştır: writeExternal () ve readExternal ().

Serileştirme ve seriyi kaldırma için Haricileştirilebilir arabirimi kullanırken, geliştiricilerin writeExternal () ve readExternal () yöntemlerini yeniden yazmaları gerekir. Aksi takdirde, tüm değişkenlerin değerleri varsayılan değerler olacaktır.

geçici

Transient anahtar kelimenin amacı değişkenlerin serileştirilmesini kontrol etmektir.Bu anahtar kelimenin değişken bildiriminden önce eklenmesi, değişkenin dosyaya serileştirilmesini engelleyebilir.Seri durumdan çıkarıldıktan sonra, geçici değişkenin değeri başlangıç değerine ayarlanır. Örneğin, int türü 0'dır ve nesne türü boştur.

Özel serileştirme stratejisi

Serileştirme işlemi sırasında, writeObject ve readObject yöntemleri serileştirilmiş sınıfta tanımlanırsa, sanal makine, kullanıcı tanımlı serileştirme ve seriyi kaldırma işlemi gerçekleştirmek için nesne sınıfındaki writeObject ve readObject yöntemlerini çağırmaya çalışır.

Böyle bir yöntem yoksa, varsayılan çağrı, ObjectOutputStream'in defaultWriteObject yöntemi ve ObjectInputStream'in defaultReadObject yöntemidir.

Kullanıcı tanımlı writeObject ve readObject yöntemleri, kullanıcıların serileştirme sürecini kontrol etmesine olanak tanır; örneğin, serileştirilmiş değer, serileştirme işlemi sırasında dinamik olarak değiştirilebilir.

Bu nedenle, bazı özel alanlar için bir serileştirme stratejisi tanımlamanız gerektiğinde, geçici modifikasyonu kullanmayı düşünebilir ve writeObject ve readObject yöntemlerini kendiniz yeniden yazabilirsiniz.Örneğin, java.util.ArrayList'in böyle bir uygulaması vardır.

Yukarıda, bazı okuyucuların serileştirmeyle ilgili bilgilere hakim olması gerekir.

Java'da String, Integer, vb. Gibi serileştirme arabirimlerini uygulayan birkaç sınıf buluyoruz, bir ayrıntı bulabiliriz, yani Serializable uygulamasına ek olarak, bu sınıflar ayrıca bir serialVersionUID tanımlar.

Peki, serialVersionUID tam olarak nedir? Neden böyle bir alan ayarlanmalıdır?

SerialVersionUID nedir

Serileştirme, bir nesnenin durum bilgisini saklanabilen veya iletilebilen bir forma dönüştürme işlemidir. Java nesnelerinin JVM'nin yığın belleğinde depolandığını hepimiz biliyoruz, yani JVM öbek yoksa nesne kaybolacaktır.

Serileştirme, JVM kapalıyken bile nesneleri kaydetmenize izin veren bir çözüm sağlar. Tıpkı genellikle kullandığımız U disk gibi. Java nesnelerini, bir dosyada saklananlar gibi depolanabilen veya iletilebilen (ikili akış gibi) bir formda seri hale getirin. Bu şekilde, nesneye tekrar ihtiyaç duyulduğunda, ikili akış dosyadan okunur ve nesnenin ikili akıştan serileştirilmesi kaldırılır.

Sanal makinenin seriyi kaldırmaya izin verip vermediği, yalnızca sınıf yolunun ve işlev kodunun tutarlı olup olmadığına değil, aynı zamanda çok önemli bir nokta, iki sınıfın serileştirme kimliklerinin tutarlı olup olmadığıdır. , Bu sözde serileştirilmiş kimlik, kodda tanımladığımız serialVersionUID'dir.

SerialVersionUID değişirse ne olur?

Bir örnek alalım ve serialVersionUID değiştirilirse ne olacağını görelim.

Dosyaya bir Kullanıcı1 nesnesi yazmak için önce yukarıdaki kodu çalıştırıyoruz. Daha sonra User1 sınıfını değiştiriyoruz ve serialVersionUID değerini 2L olarak değiştiriyoruz.

Ardından, dosyadaki nesnelerin serisini kaldırmak için aşağıdaki kodu yürütün:

Sonuçlar aşağıdaki gibidir:

java.io.InvalidClassException: com.hollis.User1; yerel sınıf uyumsuz: stream classdesc serialVersionUID = 1, yerel sınıf serialVersionUID = 2

Yukarıdaki kodun bir java.io.InvalidClassException attığı ve serialVersionUID değerinin tutarsız olduğuna işaret ettiği bulunabilir.

Bunun nedeni, serileştirme sırasında JVM'nin, yerel ilgili varlık sınıfının serialVersionUID'si ile geçirilen bayt akışındaki serialVersionUID değerini karşılaştırmasıdır. Aynı iseler, tutarlı kabul edilirler ve serileri kaldırılabilir, aksi takdirde Tutarsız serileştirme sürümleriyle ilgili bir istisna, InvalidCastException'dur.

Bu aynı zamanda, "Alibaba Java Geliştirme Kılavuzu" nun uyumluluk yükseltmesinde, sınıfı değiştirirken serialVersionUID'yi değiştirmemesini şart koşmasının nedenidir. Tamamen uyumsuz olmadığı sürece iki versiyon . ve bu yüzden, serialVersionUID aslında sürüm tutarlılığını doğrular.

Okuyucu ilgilenirse, her sürümün JDK koduna göz atabilirsiniz.Bu geriye dönük uyumlu sınıfların serialVersionUID'si değişmemiştir. Örneğin, String sınıfının serialVersionUID'si her zaman -6849794470754667710L'dir.

Bununla birlikte, yazar bu spesifikasyonun aslında daha katı olabileceğine inanıyor, yani şunları şart koşuyor:

Bir sınıf Serializable arabirimini uyguluyorsa, manuel olarak özel bir statik son uzun serialVersionUID değişkeni eklemeniz ve başlangıç değerini ayarlamanız gerekir.

Bir serialVersionUID neden açıkça belirlenmelidir?

Sınıfta açık bir şekilde serialVersionUID tanımlamazsak ne olacağını görelim.

Yukarıdaki demo kodunu değiştirmeyi deneyin, önce bir nesneyi tanımlamak için aşağıdaki sınıfı kullanın, serialVersionUID bu sınıfta tanımlı değildir ve bunu dosyaya yazın.

Daha sonra User1 sınıfını değiştirip ona bir nitelik ekliyoruz. Dosyadan okumaya ve serisini kaldırmaya çalışıyorum.

Sonuçları:

java.io.InvalidClassException: com.hollis.User1; yerel sınıf uyumsuz: stream classdesc serialVersionUID = -2986778152837257883, yerel sınıf serialVersionUID = 7961728318907695402

Benzer şekilde, bir InvalidClassException atıldı ve iki serialVersionUID'nin farklı olduğu, yani -2986778152837257883 ve 7961728318907695402 olduğu belirtildi.

Buradan sistemin bir serialVersionUID eklemiş olduğu görülebilir.

Bu nedenle, sınıf Serializable'ı uyguladıktan sonra, bir serialVersionUID tanımlamanız önerilir. Aksi takdirde, sınıf değiştirildiğinde bir istisna ortaya çıkar.

SerialVersionUID'yi görüntülemenin iki yolu vardır:

Biri varsayılan 1L'dir, örneğin:

özel statik son uzun serialVersionUID = 1L;

Diğeri, sınıf adı, arabirim adı, üye yöntemi ve öznitelikleri vb. Temelinde 64 bitlik bir karma alanı oluşturmaktır, örneğin:

özel statik son uzun serialVersionUID = xxxxL;

İkinci yöntem, daha sonra tanıtılacak olan IDE'nin yardımıyla oluşturulabilir.

Arkasındaki ilke

Bunu biliyorsanız, kaynak koduna bir göz atalım ve serialVersionUID değiştiğinde neden bir istisna atıldığını inceleyelim. Net bir tanım olmadığında varsayılan serialVersionUID nasıl gelir?

Kod miktarını basitleştirmek için serileştirme çağrı zinciri aşağıdaki gibidir:

İnitNonProxy'de anahtar kodu aşağıdaki gibidir:

Serileştirme işleminde, serialVersionUID karşılaştırılır ve eşit olmadığı bulunursa, doğrudan bir istisna atılır.

GetSerialVersionUID yöntemine daha yakından bakın:

SerialVersionUID tanımlanmadığında, computeDefaultSUID yöntemi, varsayılan bir serialVersionUID oluşturmak için çağrılır.

Bu aynı zamanda yukarıdaki iki sorunun temel nedenini de buldu.Aslında, kod kesinlikle doğrulandı ve tanımlanmadığında bir serialVersionUID otomatik olarak üretildi.

IDEA istemi

SerialVersionUID'yi tanımlamayı unutmadığımızdan emin olmak için Intellij IDEA'nın konfigürasyonunu ayarlayabiliriz.Serializable arayüz uygulandıktan sonra serialVersionUID tanımlanmamışsa IDEA (eclipse gibi):

Ve tek tıklama ile bir tane oluşturabilir:

Elbette, bu konfigürasyon varsayılan olarak etkili değildir, IDEA'da manuel olarak ayarlamanız gerekir:

Şekilde 3 ile işaretlenmiş yerde (serialVersionUID konfigürasyonu olmayan Serileştirilebilir sınıf), işaretleyin ve kaydedin.

sonuç olarak

serialVersionUID, sürüm tutarlılığını doğrulamak için kullanılır. ve bu yüzden Uyumluluk yükseltmeleri yaparken, sınıftaki serialVersionUID değerini değiştirmeyin.

Özellikle, bu makalenin başlığı bu makalenin tüm içeriğini tam olarak ifade etmediği için, tekrar vurgulayacağım: serialVersionUID Sürüm tutarlılığını doğrulamak olduğundan, sürüm yükseltmeleri (uyumsuz yükseltmeler) yaparken, serileştirme karışıklığını önlemek için bu alanın değerini değiştirmeyi unutmayın.

Bir sınıf Serializable arabirimini uygularsa, serialVersionUID'yi tanımlamayı unutmamalısınız, aksi takdirde bir istisna oluşur. Yardım istemesine izin vermek için IDE'de ayarlayabilir ve tek bir tıklama ile hızlı bir şekilde bir serialVersionUID oluşturabilirsiniz.

İstisna, doğrulama serileştirme işlemi sırasında yapıldığından ve açıkça tanımlanmadıysa, sınıf adı ve özniteliklerine göre otomatik olarak oluşturulacağı için oluşur.

api izleme sistemi - apimonitor
önceki
Düşük kodlu hızlı geliştirme platformu JEPaaS
Sonraki
Tam bağlantı izleme: çözüme genel bakış ve karşılaştırma | gerçekten kuru
hanbo-push dağıtılmış mesaj push, IM servisi
Ali Great God, mikro hizmet mimarisindeki API ağ geçidi uygulamasını paylaşıyor
mallcloud-platform, springboot bulutuna dayalı bir alışveriş merkezi projesidir
MyBatis bu 9 tasarım modelini içerir, kaç tanesini biliyorsunuz?
Hurricane Sheep Knife Ashe için standart hale geldi, Polar Ranger Genting'e hükmediyor
Çift sunucu beş güçlü en iyi tek envanter: Nuoshou Jianji güçlü bir şekilde hakim
Her şeye gücü yeten kan emici teknoloji silahı ana akım haline geldi ve ejderha kızın dul eşi bir kaplan gibi
9.18 National Service Tüm Kahramanlar TOP5 oranını kazanır, iki AP ormancısı hakimdir
Ulusal hizmetin 9.18 versiyonundaki beş popüler ormancı geri döndü
9.18 Ulusal hizmetteki beş sallanan kahraman, silah ustası Ueno'ya hakim
Kutupsal elementler buzu ve ateşi gökyüzünü ikiye katlar
To Top