JavaRush /Java Blogu /Random-AZ /Java-da serializasiya və seriyadan çıxarma

Java-da serializasiya və seriyadan çıxarma

Qrupda dərc edilmişdir
Salam! Bugünkü mühazirəmizdə Java-da serializasiya və deserializasiya haqqında danışacağıq. Sadə bir nümunə ilə başlayaq. Tutaq ki, siz kompüter oyununun yaradıcısısınız. Əgər siz 90-cı illərdə böyümüsünüzsə və o dövrlərin oyun konsollarını xatırlayırsınızsa, yəqin ki, bilirsiniz ki, onlar bu gün açıq-aşkar bir şeyə malik deyildilər - oyun saxlamaq və yükləmək :) Yoxsa... təsəvvür edin! Qorxuram ki, bu gün belə imkanı olmayan oyun uğursuzluğa məhkum olacaq! Və əslində, oyunu "saxlamaq" və "yükləmək" nədir? Yaxşı, adi mənada bunun nə olduğunu başa düşürük: oyunu keçən dəfə qaldığımız yerdən davam etdirmək istəyirik. Bunu etmək üçün bir növ "yoxlama nöqtəsi" yaradırıq, sonra oyunu yükləmək üçün istifadə edirik. Bəs bu, gündəlik mənada deyil, “proqramçı” mənasında nə deməkdir? Cavab sadədir: proqramımızın vəziyyətini saxlayırıq. Deyək ki, siz İspaniya üçün strategiya oyunu oynayırsınız. Oyununuzun dövləti var: kimin hansı ərazilərə sahib olması, kimin nə qədər resursu, kimin kiminlə ittifaqda olması və kimin əksinə, müharibə şəraitində olması və s. Bu məlumat, proqramımızın vəziyyəti sonradan məlumatları bərpa etmək və oyunu davam etdirmək üçün bir şəkildə saxlanmalıdır. Serializasiyasıradan çıxarma mexanizmləri məhz bunun üçün istifadə olunur . Serializasiya obyektin vəziyyətinin bayt ardıcıllığına saxlanması prosesidir. Deserializasiya bu baytlardan obyektin yenidən qurulması prosesidir. İstənilən Java obyekti bayt ardıcıllığına çevrilir. Bu nə üçündür? Biz dəfələrlə demişik ki, proqramlar öz-özünə mövcud deyil. Çox vaxt onlar bir-biri ilə qarşılıqlı əlaqədə olur, məlumat mübadiləsi aparır və s. Bunun üçün bayt formatı rahat və səmərəlidir. Biz, məsələn, sinif obyektimizi SavedGame(saxlanılan oyunu) bayt ardıcıllığına çevirə, həmin baytları şəbəkə üzərindən başqa kompüterə köçürə və ikinci kompüterdə həmin baytları yenidən Java obyektinə çevirə bilərik! Eşitmək çətindir, elə deyilmi? Görünür, bu prosesi təşkil etmək asan olmayacaq :/ Xoşbəxtlikdən, yox! :) Java-da Serializable interfeysi serializasiya proseslərinə cavabdehdir . Bu interfeys son dərəcə sadədir: onu istifadə etmək üçün tək bir üsul tətbiq etməyə ehtiyac yoxdur! Saxlama oyun sinifimiz belə görünəcək:
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) +
               '}';
   }
}
Üç məlumat dəsti ərazilər, iqtisadiyyat və diplomatiya haqqında məlumat üçün cavabdehdir və Seriallaşdırıla bilən interfeys Java maşınına deyir: " Hər şey qaydasındadır, əgər bir şey varsa, bu sinif obyektləri seriallaşdırıla bilər ." Heç bir metodu olmayan interfeys qəribə görünür :/ Niyə lazımdır? Bu sualın cavabı yuxarıdadır: yalnız Java maşınına lazımi məlumatları təqdim etmək üçün. Əvvəlki mühazirələrdən birində biz qısaca olaraq marker interfeyslərindən bəhs etdik. Bunlar, sadəcə olaraq gələcəkdə Java maşını üçün faydalı olacaq əlavə məlumatlarla dərslərimizi qeyd edən xüsusi informativ interfeyslərdir. Onların həyata keçirilməsi lazım olan heç bir üsulları yoxdur. Beləliklə, Serializable belə interfeyslərdən biridir. private static final long serialVersionUIDDigər vacib məqam: sinifdə müəyyən etdiyimiz dəyişən . Niyə lazımdır? Bu sahədə seriallaşdırılmış sinfin unikal versiya identifikatoru var . Serializable interfeysini tətbiq edən istənilən sinif versiya identifikatoruna malikdir. O, sinfin məzmunu - sahələr, bəyannamə qaydası, metodlar əsasında hesablanır. Və əgər biz sinifimizdə sahə tipini və/yaxud sahələrin sayını dəyişdirsək, versiya identifikatoru dərhal dəyişəcək. serialVersionUID də sinif seriallaşdırıldıqda yazılır. Seriyadan çıxarmağa, yəni bayt dəstindən obyekti bərpa etməyə çalışdığımız zaman dəyər proqramımızdakı sinfin serialVersionUIDdəyəri ilə müqayisə edilir . serialVersionUIDDəyərlər uyğun gəlmirsə, java.io.InvalidClassException atılacaq. Bunun bir nümunəsini aşağıda görəcəyik. Belə halların qarşısını almaq üçün biz sadəcə olaraq sinifimiz üçün bu versiya identifikatorunu əl ilə təyin edirik. Bizim vəziyyətimizdə o, sadəcə olaraq 1-ə bərabər olacaq (istədiyiniz hər hansı digər nömrəni əvəz edə bilərsiniz). Yaxşı, obyektimizi seriallaşdırmağa cəhd etməyin SavedGamevə nə baş verdiyini görməyin vaxtı gəldi!
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 position of neutrality"};

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

       //create 2 threads 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 game to file
       objectOutputStream.writeObject(savedGame);

       // close the stream and release resources
       objectOutputStream.close();
   }
}
Gördüyünüz kimi, biz 2 mövzu yaratdıq - FileOutputStreamObjectOutputStream. Onlardan birincisi verilənləri fayla yaza bilir, ikincisi isə obyektləri baytlara çevirə bilir. Siz artıq oxşar “iç-içə” konstruksiyaları görmüsünüz, məsələn, new BufferedReader(new InputStreamReader(...))əvvəlki mühazirələrdə, ona görə də onlar sizi qorxutmamalıdır :) İki ipdən ibarət belə bir “zəncir” yaratmaqla biz hər iki vəzifəni yerinə yetiririk: obyekti SavedGamebayt dəstinə çeviririk. və metoddan istifadə edərək faylda saxlayın writeObject(). Yeri gəlmişkən, əldə etdiklərimizi belə yoxlamadıq! Fayla baxmaq vaxtıdır! *Qeyd: faylın əvvəlcədən yaradılmasına ehtiyac yoxdur. Əgər bu adda fayl yoxdursa, o, avtomatik olaraq yaradılacaq* Və onun məzmunu budur! ¬н sr SavedGame [ diplomatacyInfot [Ljava/lang/String;[ resurslarInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТVзй{G xp t pР¤СЂР°РЅС†РІРѕСЋРµС РЅС†РІРѕСЋРμС РЃСЃР˃СРЃСЃР ° РЅРёСЏ Р·Р°Ряла позицию нейтралитетаuq ~ t "РЈ Р˜СЃРїР°РЅРёРёР†РёРё 10Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Р˜СЃРїР°РЅР˜СЃРїР°РЅР˜СЃРїР°РЅРРРёРёР %ЂииР6РЈ Ёранции Ј Р РѕСЃСЃРёРё 10 РїСЂРѕРІРёРЅС †РёР№t &РЈ ФранцРеРё 8 проввинций Oops :( Deyəsən proqramımız işləmədi :( Əslində, o işlədi. Yadınızdadır ki, biz fayla tam olaraq bir sıra bayt köçürdük. sadəcə obyekt və ya mətn deyil?Yaxşı, bu dəst belə görünür :) Bu, bizim saxladığımız oyunumuzdur!Əsl obyektimizi bərpa etmək, yəni oyunu yükləyib qaldığımız yerdən davam etdirmək istəyiriksə, bizə lazım olan tərs proses , sıradan çıxarma ... Bizim vəziyyətimizdə belə görünəcək:
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);
   }
}
Və nəticə budur! SavedGame{territoriesInfo=[İspaniyanın 6 vilayəti, Rusiyanın 10 vilayəti, Fransanın 8 əyaləti var], resourcesInfo=[İspaniyanın 100 qızılı, Rusiyanın 80 qızılı, Fransanın 90 qızılı var], diplomatacyInfo=[Fransa Rusiya ilə müharibə edir, İspaniya neytral mövqe tutdu]} Əla! Əvvəlcə oyunumuzun vəziyyətini bir faylda saxlamağa, sonra onu fayldan bərpa etməyə müvəffəq olduq. İndi eyni şeyi etməyə çalışaq, lakin SavedGameversiya identifikatorunu sinfimizdən çıxarın. Hər iki sinfimizi yenidən yazmayacağıq, onlardakı kod eyni olacaq, sadəcə SavedGamesiləcəyik private static final long serialVersionUID. Serializasiyadan sonra obyektimiz belədir: ¬н sr SavedGameі€MіuОm‰ [ diplomatacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТВзй{G xp t pФраняаиѽц ‚ СЃ РРоссией, Р˜СЃРїР°РЅРЏР·Р°РЅСЏР»Р° RїРѕР·РёС†РёСЋ нейтралитралитралитрРˈР˜Р˚Р˜С ёРё 100 Р · олотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЃё Р˅ RЃё Р˜ їСЂРѕРІРёРЅС† РёР№t %РЈ Р РѕСЃСЃРёРё 10 провнинцийt &РЈ Франции 8 РЈРЅРЅ-ə cəhd edərkən onu seriallaşdırın, belə oldu: InvalidClassException: yerli sinif uyğun deyil: stream classdesc serialVersionUID = - 196410440475012755, yerli sinif serialVersionUID = -6675950253085108747 Bu yuxarıda qeyd olunan eyni istisnadır.Bu barədə tələbələrimizdən birinin məqaləsində oxuya bilərsiniz.Yeri gəlmişkən, bir vacib məqamı qaçırmışıq.Aydındır ki, strings və primitiv asanlıqla seriallaşdırılır: Java, əlbəttə ki, bəzilərinə malikdir - bunun üçün daxili mexanizmlər var. Bəs əgər bizim serializablesinifdə primitivlər kimi deyil, digər obyektlərə istinadlar kimi ifadə olunan sahələr varsa necə? Məsələn , sinifimizlə işləmək TerritoriesInfoüçün ayrıca siniflər yaradaq . ResourcesInfoDiplomacyInfoSavedGame
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 + '\'' +
               '}';
   }
}
Ancaq indi bir sualımız var: dəyişdirilmiş sinfi seriallaşdırmaq istəyiriksə, bütün bu siniflər Seriallaşdırıla bilərmi SavedGame?
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 +
               '}';
   }
}
Yaxşı, gəlin bunu praktikada yoxlayaq! Gəlin hər şeyi olduğu kimi buraxaq və obyekti seriallaşdırmağa çalışaq SavedGame:
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
       TerritoriesInfo territoriesInfo = new TerritoriesInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourcesInfo resourcesInfo = new ResourcesInfo("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 position of neutrality");


       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();
   }
}
Nəticə: "main" java.io.NotSerializable mövzuda istisna istisna: DiplomacyInfo alınmadı! Əslində sualımızın cavabı buradadır. Obyekti seriallaşdırdığınız zaman onun nümunə dəyişənlərində istinad etdiyi bütün obyektlər seriallaşdırılır. Əgər həmin obyektlər üçüncü obyektlərə də istinad edirsə, onlar da seriallaşdırılır. Və s sonsuza qədər. Bu zəncirdəki bütün siniflər Seriallaşdırıla bilən olmalıdır, əks halda onlar seriallaşdırıla bilməyəcək və istisna atılacaq. Bu, yeri gəlmişkən, gələcəkdə problemlər yarada bilər. Məsələn, seriallaşdırma zamanı sinfin bir hissəsinə ehtiyacımız yoxdursa, nə etməliyik? Yaxud, məsələn, TerritoryInfohansısa kitabxananın bir hissəsi kimi proqramımızda bir sinfi miras aldıq. Bununla belə, o, seriyalaşdırıla bilməz və buna görə də biz onu dəyişdirə bilmərik. Belə çıxır ki, biz TerritoryInfosinifimizə sahə əlavə edə bilmərik, çünki o zaman bütün sinif seriyalaşdırıla bilməyəcək! Problem:/ Bu cür problemlər Java-da açar sözündən istifadə etməklə həll olunur . Bu açar sözü sinif sahəsinə əlavə etsəniz, bu sahənin dəyəri seriallaşdırılmayacaq. Sinifimizin sahələrindən birini etməyə çalışaq , bundan sonra bir obyekti seriallaşdırıb bərpa edəcəyik. SavedGameSavedGameJava-da serializasiya və seriyadan çıxarma - 2transientSavedGame transient
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
       TerritoriesInfo territoriesInfo = new TerritoriesInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourcesInfo resourcesInfo = new ResourcesInfo("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 position of neutrality");


       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();


   }
}
Və nəticə budur: SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='İspaniyanın 100 qızılı, Rusiyanın 80 qızılı, Fransanın 90 qızılı var'}, diplomatacyInfo=DiplomacyInfo{info='Fransa Rusiya ilə müharibədədir, İspaniya neytral mövqe tutdu'}}transient Eyni zamanda -sahasına hansı dəyər veriləcək sualına cavab aldıq . Ona standart dəyər təyin edilir. Obyektlərə münasibətdə bu belədir null. Boş vaxtlarınızda seriallaşdırma haqqında bu əla məqaləni oxuya bilərsiniz . ExternalizableBu, həmçinin növbəti mühazirədə danışacağımız interfeysdən bəhs edir . Bundan əlavə, “Head-First Java” kitabında bu mövzuda bir fəsil var, ona diqqət yetirin :)
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION