CodeGym /Java Blogu /Rastgele /Java'da serileştirme ve seri kaldırma arasındaki fark ned...
John Squirrels
Seviye
San Francisco

Java'da serileştirme ve seri kaldırma arasındaki fark nedir?

grupta yayınlandı
MERHABA! Bugünün dersinde, Java'da serileştirme ve seriden kaldırma hakkında konuşacağız. Basit bir örnekle başlayacağız. Diyelim ki bir bilgisayar oyunu yarattınız. 90'larda büyüdüyseniz ve o dönemin oyun konsollarını hatırlıyorsanız, muhtemelen bugün hafife aldığımız bir şeye sahip olmadıklarını biliyorsunuzdur - oyunları kaydetme ve yükleme yeteneği :) Değilse, bunu hayal edin! Java'da serileştirme ve seri kaldırma arasındaki fark nedir?  - 1 Korkarım ki bugün bu yeteneklere sahip olmayan bir oyun başarısız olmaya mahkumdur! Yine de bir oyunu "kaydetmek" ve "yüklemek" ne anlama geliyor? Eh, sıradan anlamı anlıyoruz: oyuna kaldığımız yerden devam etmek istiyoruz. Bunu yapmak için, oyunu yüklemek için kullandığımız bir tür "kontrol noktası" oluşturuyoruz. Ancak bu, sıradan bir oyuncu yerine bir programcı için ne anlama geliyor? Cevap basit: biz'. Diyelim ki Strategium'da İspanya olarak oynuyorsunuz. Oyununuzun bir durumu vardır: kimin hangi bölgeye sahip olduğu, kimin kaç kaynağa sahip olduğu, kiminle ittifak içinde olduğu, kiminle savaşta olduğu vb. Gelecekte geri yüklemek ve oyuna devam etmek için bu bilgileri, programımızın durumunu bir şekilde kaydetmemiz gerekir. Çünkü serileştirme ve serileştirme tam olarak bunun içindir. Serileştirme , bir nesnenin durumunu bir bayt dizisinde saklama işlemidir. seri kaldırmabu baytlardan bir nesneyi geri yükleme işlemidir. Herhangi bir Java nesnesi bir bayt dizisine dönüştürülebilir. Buna neden ihtiyacımız olsun ki? Programların kendi başlarına var olmadıklarını defalarca söyledik. Çoğu zaman, diğer programlarla etkileşime girer, veri alışverişi yapar, vb. Ve bir bayt dizisi, uygun ve verimli bir formattır. SavedGameÖrneğin , nesnemizi bir bayt dizisine dönüştürebilir, bu baytları ağ üzerinden başka bir bilgisayara gönderebilir ve ardından ikinci bilgisayarda bu baytları tekrar bir Java nesnesine dönüştürebiliriz ! Kulağa zor geliyor, değil mi? Ve bu süreci uygulamak sancılı görünüyor :/ Neyse ki, bu öyle değil! :) Java'da,Serializableinterface seri hale getirme işleminden sorumludur. Bu arabirim son derece basittir: onu kullanmak için tek bir yöntem uygulamanıza gerek yoktur! Oyun kurtaran dersimiz bu kadar basit görünüyor:

import java.io.Serializable;
import java.util.Arrays;

public class SavedGame implements Serializable {

   private static final long serialVersionUID = 1L;

   private String[] territoriesInfo;
   private String[] resourcesInfo;
   private String[] diplomacyInfo;

   public SavedGame(String[] territoriesInfo, String[] resourcesInfo, String[] diplomacyInfo){
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public String[] getTerritoriesInfo() {
       return territoriesInfo;
   }

   public void setTerritoriesInfo(String[] territoriesInfo) {
       this.territoriesInfo = territoriesInfo;
   }

   public String[] getResourcesInfo() {
       return resourcesInfo;
   }

   public void setResourcesInfo(String[] resourcesInfo) {
       this.resourcesInfo = resourcesInfo;
   }

   public String[] getDiplomacyInfo() {
       return diplomacyInfo;
   }

   public void setDiplomacyInfo(String[] diplomacyInfo) {
       this.diplomacyInfo = diplomacyInfo;
   }

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoriesInfo=" + Arrays.toString(territoriesInfo) +
               ", resourcesInfo=" + Arrays.toString(resourcesInfo) +
               ", diplomacyInfo=" + Arrays.toString(diplomacyInfo) +
               '}';
   }
}
Üç dizi, bölgeler, kaynaklar ve diplomasi hakkındaki bilgilerden sorumludur. Seri hale getirilebilir arabirim, Java sanal makinesine şunu bildirir: " Her şey yolunda — gerekirse, bu sınıftaki nesneler serileştirilebilir ". Tek bir arayüzü olmayan bir arayüz garip görünüyor :/ Neden gerekli? Bu sorunun cevabı yukarıda görülebilir: sadece Java sanal makinesine gerekli bilgileri sağlamaya hizmet eder. Daha önceki derslerimizden birinde belirteç arayüzlerinden kısaca bahsetmiştik . Bunlar, sınıflarımızı gelecekte Java makinesi için faydalı olacak ek bilgilerle işaretleyen özel bilgi arayüzleridir. Uygulamanız gereken herhangi bir yöntemleri yok.Serializablebu arayüzlerden biridir. private static final long serialVersionUIDBir diğer önemli nokta: Sınıfta tanımladığımız değişkene neden ihtiyacımız var ? Neden gerekli? Bu alan, serileştirilmiş sınıfın sürümü için benzersiz bir tanımlayıcı içerir . Arayüzü uygulayan herhangi bir sınıfın Serializablebir tanımlayıcısı vardır version. Sınıfın içeriğine göre hesaplanır: alanları, bildirim sıraları, yöntemler, vb. Alanın türünü ve/veya sınıfımızdaki alan sayısını değiştirirsek, sürüm tanımlayıcısı hemen değişir . serialVersionUIDsınıf tefrika edildiğinde de yazılır. Seri durumdan çıkarmaya çalıştığımızda, yani bir nesneyi bir bayt kümesinden geri yüklemeye çalıştığımızda, ilişkili serialVersionUIDdeğer ile karşılaştırılır.serialVersionUIDprogramımızdaki sınıf için. Değerler eşleşmezse, bir java.io. InvalidClassException fırlatılacak. Bunun bir örneğini aşağıda göreceğiz. Bundan kaçınmak için, sürüm tanımlayıcısını sınıfımızda manuel olarak ayarlamamız yeterlidir. Bizim durumumuzda, basitçe 1'e eşit olacaktır (ancak yerine istediğiniz başka bir sayı koyabilirsiniz). Pekala, nesnemizi serileştirmeye çalışmanın SavedGameve ne olacağını görmenin zamanı geldi!

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

   public static void main(String[] args) throws IOException {

       // Create our object
       String[] territoryInfo = {"Spain has 6 provinces", "Russia has 10 provinces", "France has 8 provinces"};
       String[] resourcesInfo = {"Spain has 100 gold", "Russia has 80 gold", "France has 90 gold"};
       String[] diplomacyInfo = {"France is at war with Russia, Spain has taken a neutral position"};

       SavedGame savedGame = new SavedGame(territoryInfo, resourcesInfo, diplomacyInfo);

       // Create 2 streams to serialize the object and save it to a file
       FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

       // Save the game to a file
       objectOutputStream.writeObject(savedGame);

       // Close the stream and free resources
       objectOutputStream.close();
   }
}
Gördüğünüz gibi 2 akış oluşturduk: FileOutputStreamve ObjectOutputStream. İlki bir dosyaya veri yazabilir ve ikincisi nesneleri baytlara dönüştürür. Örneğin önceki derslerde benzer "iç içe geçmiş" yapılar gördünüz new BufferedReader(new InputStreamReader(...)), bu yüzden bunlar sizi korkutmamalı :) İki akıştan oluşan böyle bir "zincir" oluşturarak, her iki görevi de yerine getiriyoruz: nesneyi bir kümeye SavedGamedönüştürüyoruz bayt ve yöntemi kullanarak bir dosyaya kaydedin writeObject(). Ve bu arada, elimizdekilere bakmadık bile! Dosyaya bakma zamanı! *Not: Dosyayı önceden oluşturmanız gerekmez. Bu isimde bir dosya yoksa otomatik olarak oluşturulacaktır* Ve işte içeriği!

¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;­ТVзй{G xp t pФранция воюет СЃ Россией, Испания заняла позицию нейтралитетаuq ~ t "РЈ Испании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Испании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
Uh-oh :( Görünüşe göre programımız çalışmadı :( Aslında işe yaradı. Dosyaya yalnızca bir nesne veya metin değil, bir dizi bayt gönderdiğimizi hatırlıyor musunuz? bayt kümesi şuna benziyor :) Bu bizim kayıtlı oyunumuz! Orijinal nesnemizi geri yüklemek istiyorsak, yani oyuna kaldığımız yerden başlayıp devam etmek istiyorsak, ters işleme ihtiyacımız var: seriyi kaldırma . İşte bizim oyunumuzda nasıl görüneceği dava:

import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

       SavedGame savedGame = (SavedGame) objectInputStream.readObject();

       System.out.println(savedGame);
   }
}
Ve işte sonuç!

SavedGame{territoriesInfo=["Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces], resourcesInfo=[Spain has 100 gold, Russia has 80 gold, France has 90 gold], diplomacyInfo=[France is at war with Russia, Spain has taken a neutral position]}
Harika! Önce oyunumuzun durumunu bir dosyaya kaydetmeyi ve ardından dosyadan geri yüklemeyi başardık. Şimdi aynı şeyi sınıfımızın sürüm tanıtıcısı olmadan yapmaya çalışalım SavedGame. Her iki sınıfımızı da yeniden yazmayacağız. private static final long serialVersionUIDKodları aynı kalacak, ancak sınıftan çıkaracağız SavedGame. İşte serileştirmeden sonraki nesnemiz:

¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;­ТVзй{G xp t pФранция воюет СЃ Россией, Испания заняла позицию нейтралитетаuq ~ t "РЈ Испании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Испании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
Ancak seriyi kaldırmaya çalıştığımızda ne olduğuna bir bakın:

InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Yukarıda bahsettiğimiz istisna budur. Bu arada, önemli bir şeyi atladık. Dizelerin ve ilkellerin kolayca serileştirilebilmesi mantıklıdır: Java muhtemelen bunu yapmak için bir tür yerleşik mekanizmaya sahiptir. Peki ya sınıfımızda serializableilkel olmayan alanlar varsa, bunun yerine diğer nesnelere referanslar varsa? Örneğin, sınıfımızla çalışmak için ayrı TerritoriesInfove sınıflar oluşturalım ResourcesInfo.DiplomacyInfoSavedGame

public class TerritoriesInfo {
  
   private String info;

   public TerritoriesInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "TerritoriesInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}

public class ResourcesInfo {

   private String info;

   public ResourcesInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "ResourcesInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}

public class DiplomacyInfo {

   private String info;

   public DiplomacyInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "DiplomacyInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}
Ve şimdi bir soru ortaya çıkıyor: değiştirilmiş sınıfımızı seri hale getirmek istiyorsak, tüm bu sınıfların olması gerekiyor mu ?SerializableSavedGame

import java.io.Serializable;
import java.util.Arrays;

public class SavedGame implements Serializable {

   private TerritoriesInfo territoriesInfo;
   private ResourcesInfo resourcesInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public TerritoriesInfo getTerritoriesInfo() {
       return territoriesInfo;
   }

   public void setTerritoriesInfo(TerritoriesInfo territoriesInfo) {
       this.territoriesInfo = territoriesInfo;
   }

   public ResourcesInfo getResourcesInfo() {
       return resourcesInfo;
   }

   public void setResourcesInfo(ResourcesInfo resourcesInfo) {
       this.resourcesInfo = resourcesInfo;
   }

   public DiplomacyInfo getDiplomacyInfo() {
       return diplomacyInfo;
   }

   public void setDiplomacyInfo(DiplomacyInfo diplomacyInfo) {
       this.diplomacyInfo = diplomacyInfo;
   }

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoriesInfo=" + territoriesInfo +
               ", resourcesInfo=" + resourcesInfo +
               ", diplomacyInfo=" + diplomacyInfo +
               '}';
   }
}
Pekala, test edelim! Her şeyi olduğu gibi bırakalım ve bir SavedGamenesneyi serileştirmeye çalışalım:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

   public static void main(String[] args) throws IOException {

       // Create our object
       TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
       DiplomacyInfo diplomacyInfo =  new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");


       SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}
Sonuç:

Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
İşe yaramadı! Temel olarak, sorumuzun cevabı bu. Bir nesne serileştirildiğinde, örnek değişkenleri tarafından başvurulan tüm nesneler serileştirilir. Ve eğer bu nesneler başka nesnelere de referans veriyorsa, onlar da serileştirilir. Ve böylece sonsuza kadar. Bu zincirdeki tüm sınıflar olmalıdırSerializable , aksi takdirde onları seri hale getirmek imkansız olacak ve bir istisna atılacaktır. Bu arada, bu ileride sorun yaratabilir. Örneğin serileştirdiğimizde bir sınıfın parçasına ihtiyacımız yoksa ne yapmalıyız? Veya, örneğin, TerritoryInfosınıf bize üçüncü taraf bir kitaplığın parçası olarak gelirse ne olur? Ayrıca öyle olmadığını Serializableve dolayısıyla değiştiremeyeceğimizi varsayalım. TerritoryInfoBir alan ekleyemediğimiz ortaya çıktı.SavedGamesınıf, çünkü bunu yapmak tüm SavedGamesınıfı serileştirilemez hale getirir! Bu bir problem :/ Java'da serileştirme ve seri kaldırma arasındaki fark nedir?  - 2Java'da bu tür problemler transientanahtar kelime kullanılarak çözülür. Bu anahtar kelimeyi sınıfınızın bir alanına eklerseniz, o alan serileştirilmez. Sınıfın örnek alanlarından birini geçici yapmaya çalışalım SavedGame. Ardından bir nesneyi serileştirip geri yükleyeceğiz.

import java.io.Serializable;

public class SavedGame implements Serializable {

   private transient TerritoriesInfo territoriesInfo;
   private ResourcesInfo resourcesInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   // ...getters, setters, toString()
}



import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

   public static void main(String[] args) throws IOException {

       // Create our object
       TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
       DiplomacyInfo diplomacyInfo =  new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");


       SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}


import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

       SavedGame savedGame = (SavedGame) objectInputStream.readObject();

       System.out.println(savedGame);

       objectInputStream.close();


   }
}
Ve işte sonuç:

SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='Spain has 100 gold, Russia has 80 gold, France has 90 gold'}, diplomacyInfo=DiplomacyInfo{info='France is at war with Russia, Spain has taken a neutral position'}}
Ek olarak, bir alana hangi değer atanır sorusuna da yanıt aldık transient. Varsayılan değer atanır. Nesneler için bu null. Ayıracak birkaç dakikanız olduğunda serileştirmeyle ilgili bu mükemmel makaleyi okuyabilirsiniz . ExternalizableBir sonraki derste bahsedeceğimiz arayüzden de bahsediyor . Ek olarak, "Head-First Java" kitabında bu konuyla ilgili bir bölüm vardır. biraz dikkat :)
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION