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

Java'da serileştirme ve seri kaldırma

grupta yayınlandı
MERHABA! Bugünün dersinde, Java'da serileştirme ve seriden çıkarma hakkında konuşacağız. Basit bir örnekle başlayacağız. Bir bilgisayar oyunu geliştiricisi olduğunuzu hayal edin. 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 seri hale getirme ve seriyi kaldırma - 1Korkarım bugün bu yeteneklere sahip olmayan bir oyun başarısız olmaya mahkumdur! Her neyse, bir oyunu 'kaydetmek' ve 'yüklemek' ne anlama geliyor? Günlük anlamı anlıyoruz: oyuna kaldığımız yerden devam etmek istiyoruz. Bunu yapmak için, daha sonra oyunu yüklemek için kullanacağımız belirli bir 'kontrol noktası' oluşturuyoruz. Ancak bu, sıradan bir oyuncu yerine bir programcı için ne anlama geliyor? Cevap basit: programımızın durumunu kaydediyoruz. Diyelim ki bir strateji oyununda İspanya oynuyorsunuz. Oyununuzun durumu vardır: herkesin hangi bölgelere sahip olduğu, herkesin ne kadar kaynağa sahip olduğu, hangi ittifakların var olduğu ve kiminle olduğu, kimin savaşta olduğu vb. Bu bilgiler, programımızın durumu, verileri geri yüklemek ve oyuna devam etmek için bir şekilde kaydedilmelidir. Olduğu gibi, Java'da serileştirme, bir nesnenin durumunu bir bayt dizisi olarak kaydetme işlemidir. Java'da serileştirme, bir nesneyi bu baytlardan geri yükleme işlemidir. Herhangi bir Java nesnesi bir bayt dizisine dönüştürülebilir. buna neden ihtiyacımız var? Programların kendi başlarına var olmadıklarını defalarca söyledik. Çoğunlukla birbirleriyle etkileşime girerler, veri alışverişinde bulunurlar vs. Bunun için bir bayt formatı uygun ve verimlidir. Örneğin, SavedGame'imizin bir nesnesini dönüştürebiliriz.sınıfı bir bayt dizisine dönüştürün, bu baytları ağ üzerinden başka bir bilgisayara aktarın ve sonra diğer bilgisayarda bu baytları tekrar bir Java nesnesine dönüştürün! Kulağa zor geliyor, değil mi? Tüm bunları gerçekleştirmek zor olacak gibi görünüyor: / Neyse ki, durum bu değil! :) Java'da Seri hale getirilebilir arabirim, seri hale getirme işleminden sorumludur. Bu arayüz son derece basittir: Kullanmak için tek bir yöntem uygulamak zorunda değilsiniz! Oyun kaydetme sınıfımızın ne kadar basit olduğuna bakın:

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

public class SavedGame implements Serializable {

   private static final long serialVersionUID = 1L;

   private String[] territoryInfo;
   private String[] resourceInfo;
   private String[] diplomacyInfo;

   public SavedGame(String[] territoryInfo, String[] resourceInfo, String[] diplomacyInfo){
       this.territoryInfo = territoryInfo;
       this.resourceInfo = resourceInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public String[] getTerritoryInfo() {
       return territoryInfo;
   }

   public void setTerritoryInfo(String[] territoryInfo) {
       this.territoryInfo = territoryInfo;
   }

   public String[] getResourceInfo() {
       return resourceInfo;
   }

   public void setResourceInfo(String[] resourceInfo) {
       this.resourceInfo = resourceInfo;
   }

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

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

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoryInfo=" + Arrays.toString(territoryInfo) +
               ", resourceInfo=" + Arrays.toString(resourceInfo) +
               ", diplomacyInfo=" + Arrays.toString(diplomacyInfo) +
               '}';
   }
}
Bölgeler, kaynaklar ve diplomasi hakkındaki bilgilerden üç dizi sorumludur ve Serializable arabirimi Java makinesine şunu söyler: " Bu sınıftaki nesneler serileştirilebilirse her şey yolundadır ." Tek bir arayüzü olmayan bir arayüz garip görünüyor :/ Neden gerekli? Bu sorunun cevabı yukarıda verilmiştir: Sadece Java makinesine gerekli bilgileri sağlamak için gereklidir. Geçmiş bir derste, işaretçi 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. İşte Serileştirilebilir - böyle bir arayüz. İşte bir başka önemli nokta: Neden ihtiyacımız var?sınıfta tanımladığımız özel statik son uzun serialVersionUID değişkeni? Bu alan, serileştirilmiş sınıfın benzersiz sürüm tanımlayıcısını içerir. Serializable arabirimini uygulayan her sınıfın bir sürüm tanımlayıcısı vardır. Sınıf alanlarının içeriğine ve bildirim sırasına ve metotlara ve bildirim sırasına göre belirlenir. Ve sınıfımızdaki bir alan türünü ve/veya alan sayısını değiştirirsek, sürüm tanımlayıcısı anında değişir. serialVersionUID , sınıf serileştirildiğinde de yazılır. Seri durumdan çıkarmaya çalıştığımızda, yani bir bayt dizisinden bir nesneyi geri yüklemeye çalıştığımızda, serialVersionUID'nin değeri serialVersionUID'nin değeriyle karşılaştırılır.programımızdaki sınıfın Değerler eşleşmezse, bir java.io.InvalidClassException atılır. Bunun bir örneğini aşağıda göreceğiz. Bu tür durumlardan kaçınmak için, sınıfımızın sürüm tanımlayıcısını manuel olarak ayarlamamız yeterlidir. Bizim durumumuzda, basitçe 1'e eşit olacaktır (istediğiniz herhangi bir sayıyı kullanabilirsiniz). Pekala, SavedGame nesnemizi serileştirmeye çalışmanın ve 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[] resourceInfo = {"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, resourceInfo, 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 release resources
       objectOutputStream.close();
   }
}
Gördüğünüz gibi 2 akış oluşturduk: FileOutputStream ve ObjectOutputStream . Birincisi dosyaya nasıl veri yazılacağını bilir ve ikincisi nesneleri baytlara dönüştürür. Önceki derslerde benzer iç içe yapılar gördünüz, örneğin, new BufferedReader(new InputStreamReader(...)) , bu yüzden sizi korkutmamalılar :) Bu iki akış zincirini oluşturarak, her iki görevi de gerçekleştiriyoruz: SavedGame nesnesini bir bayt dizisine dönüştürüyoruz ve writeObject() yöntemini kullanarak bir dosyaya kaydediyoruz. Ve bu arada, elimizdekilere bakmadık bile! Dosyaya bakma zamanı! *Not: Dosyayı önceden oluşturmak gerekli değildir. Belirtilen ada sahip bir dosya yoksa otomatik olarak oluşturulur* Ve içeriği şöyledir: ¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[resourceInfoq ~ [ TerritoryInfoq ~ xpur [Ljava.lang. String;¬ТVзй{G xp t pФранция воюет СЃ Р РѕСЃСией, Р˜СЃРїР°РЅРё СЏ заняла позицию нейтралит етаuq ~ t "РЈ Р˜СЃРїР°РЅРёРё 100 золотаt РЈ Р РѕСЃСЃРёРё 80 Р·РѕР» отаt !РЈ Франции 90 Р·РѕР »РѕС‚Р°uq ~ t &РЈ Р˜СЃРїР°РЅРёРё 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 Eylül Oh, oh :( Görünüşe göre programımız çalışmadı : ( Aslında işe yaradı. Dosyaya sadece bir nesne veya metin değil, bir bayt dizisi gönderdiğimizi hatırlıyor musunuz? Peki, bu bayt dizisi budur benziyor :) Bu bizim kaydedilmiş 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. Bizim için şöyle görünüyor:

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{territoryInfo=[İspanya'nın 6 eyaleti var, Rusya'nın 10 eyaleti var, Fransa'nın 8 eyaleti var],sourceInfo=[İspanya'nın 100 altını var, Rusya'nın 80 altını var, Fransa'nın 90 altını var], diplomacyInfo=[Fransa Rusya ile savaş halinde, İspanya tarafsız bir pozisyon aldı]} Mükemmel! Oyunumuzun durumunu önce bir dosyaya kaydetmeyi, ardından dosyadan geri yüklemeyi başardık. Şimdi aynı şeyi yapmaya çalışalım, ancak sürüm tanımlayıcısını SavedGame sınıfımızdan kaldıracağız . Her iki sınıfımızı da yeniden yazmayacağız. Kodları aynı olacaktır. Özel statik son uzun serialVersionUID'yi SavedGame sınıfından kaldıracağız . İşte serileştirmeden sonraki nesnemiz: ¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[sourceInfoq ~ [ zoneInfoq ~ xpur [Ljava.lang.String;¬ТVзй{G xp t pФранция РІРѕСЋРµС] ‚ СЃ Россией, Hava Durumu ‚Р°uq ~ t "РЈ Р˜СЃРїР°РЅРёРё 100 золотР°t РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Р˜СЃРїР°РЅРёРё 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 РїСЂРѕРІР ёРЅС†РёР№ Ama seriyi kaldırmaya çalıştığımızda ne olduğuna bir bakın: InvalidClassException: yerel sınıf uyumsuz: stream classdesc serialVersionUID = -196410440475012755, yerel sınıf serialVersionUID = -6675950253085108747 Bu arada, önemli bir şeyi atladık. Açıkçası, dizeler ve ilkel öğeler kolayca serileştirilir: Java kesinlikle bunun için bazı yerleşik mekanizmalara sahiptir. Peki ya seri hale getirilebilir sınıfımızda ilkel olmayan alanlar varsa, bunun yerine diğer nesnelere referanslar varsa? Örneğin, SavedGame sınıfımızla çalışmak için ayrı TerritoryInfo , ResourceInfo ve DiplomacyInfo sınıfları oluşturalım .

public class TerritoryInfo {

   private String info;

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

   public String getInfo() {
       return info;
   }

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

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

public class ResourceInfo {

   private String info;

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

   public String getInfo() {
       return info;
   }

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

   @Override
   public String toString() {
       return "ResourceInfo{" +
               "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 soruyla karşı karşıyayız: SavedGame sınıfımızı serileştirmek istiyorsak, bu sınıfların tümü Serileştirilebilir olmalı mı ?

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

public class SavedGame implements Serializable {

   private TerritoryInfo territoryInfo;
   private ResourceInfo resourceInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoryInfo territoryInfo, ResourceInfo resourceInfo, DiplomacyInfo diplomacyInfo) {
       this.territoryInfo = territoryInfo;
       this.resourceInfo = resourceInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public TerritoryInfo getTerritoryInfo() {
       return territoryInfo;
   }

   public void setTerritoryInfo(TerritoryInfo territoryInfo) {
       this.territoryInfo = territoryInfo;
   }

   public ResourceInfo getResourceInfo() {
       return resourceInfo;
   }

   public void setResourceInfo(ResourceInfo resourceInfo) {
       this.resourceInfo = resourceInfo;
   }

   public DiplomacyInfo getDiplomacyInfo() {
       return diplomacyInfo;
   }

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

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoryInfo=" + territoryInfo +
               ", resourceInfo=" + resourceInfo +
               ", diplomacyInfo=" + diplomacyInfo +
               '}';
   }
}
Tamam o zaman! Test edelim! Şimdilik her şeyi olduğu gibi bırakıp bir SavedGame nesnesini serileştirmeye çalışacağız :

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(territoryInfo, resourceInfo, diplomacyInfo);

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

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}
Sonuç: "main" dizisindeki istisna java.io.NotSerializableException: DiplomacyInfo İşe yaramadı! Öyleyse sorumuzun cevabı burada. 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 sonsuza dek. Bu zincirdeki tüm sınıflar Serializable olmalıdır , aksi takdirde onları seri hale getirmek imkansız olacak ve bir istisna atılacaktır. Bu arada, bu ileride sorun yaratabilir. Örneğin, serileştirme sırasında bir sınıfın parçasına ihtiyacımız yoksa ne yapmalıyız? Veya TerritoryInfo sınıfımızı bir kitaplığın parçası olarak 'miras yoluyla' alırsak ne olur ? Ve daha ilerisini varsayalım ki 've buna göre değiştiremeyiz. Bu, SavedGame sınıfımıza bir TerritoryInfo alanı ekleyemeyeceğimiz anlamına gelir , çünkü o zaman SavedGame sınıfının tamamı serileştirilemez hale gelir! Bu bir problem: / Java'da bu tür problemler geçici anahtar kelime ile çözülür . Bu anahtar kelimeyi sınıfınızın bir alanına eklerseniz, o alan serileştirilmez. SavedGame sınıfımızın alanlarından birini geçici yapmaya çalışalım ve ardından tek bir nesneyi serileştirip geri yükleyeceğiz. Java'da serileştirme ve seri durumdan çıkarma - 2

import java.io.Serializable;

public class SavedGame implements Serializable {

   private transient TerritoryInfo territoryInfo;
   private ResourceInfo resourceInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoryInfo territoryInfo, ResourceInfo resourceInfo, DiplomacyInfo diplomacyInfo) {
       this.territoryInfo = territoryInfo;
       this.resourceInfo = resourceInfo;
       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(territoryInfo, resourceInfo, 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{territoryInfo=null, sourceInfo=ResourceInfo{info='İspanya'nın 100 altını var, Rusya'nın 80 altını var, Fransa'nın 90 altını var'}, diplomacyInfo=DiplomacyInfo{info='Fransa, Rusya ve İspanya ile savaşta tarafsız bir pozisyon aldı'}} Bununla birlikte, geçici bir alana hangi değerin atanacağı sorusuna bir yanıt aldık . Varsayılan değer atanır. Nesneler için bu null . 'Head-First Java' kitabında bu konuyla ilgili mükemmel bir bölüm okuyabilirsiniz, Dikkat edin :)
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION