JavaRush /Java блогы /Random-KK /Java тіліндегі сериялау және сериядан шығару

Java тіліндегі сериялау және сериядан шығару

Топта жарияланған
Сәлеметсіз бе! Бүгінгі дәрісте Java тіліндегі сериализация және сериясыздандыру туралы айтатын боламыз. Қарапайым мысалдан бастайық. Сіз компьютерлік ойынды жасаушысыз делік. Егер сіз 90-шы жылдары өскен болсаңыз және сол кездегі ойын консольдерін есіңізде ұстасаңыз, оларға бүгінгі күні анық нәрсе жетіспейтінін білетін шығарсыз - ойындарды сақтау және жүктеу :) Егер жоқ болса ... елестетіп көріңіз! Бүгін мұндай мүмкіндігі жоқ ойын сәтсіздікке ұшырайды деп қорқамын! Ал, шын мәнінде, ойынды «сақтау» және «жүктеу» дегеніміз не? Кәдімгі мағынада біз оның не екенін түсінеміз: біз ойынды өткен жолы тоқтаған жерімізден жалғастырғымыз келеді. Мұны істеу үшін біз ойынды жүктеу үшін қолданатын «бақылау нүктесінің» түрін жасаймыз. Бірақ бұл күнделікті мағынада емес, «бағдарламашы» мағынасында нені білдіреді? Жауап қарапайым: біз бағдарламамыздың күйін сақтаймыз. Сіз Испания үшін стратегиялық ойын ойнадыңыз делік. Сіздің ойыныңыздың күйі бар: кімнің қандай аумақтары бар, кімнің қанша ресурстары бар, кім кіммен одақтас және кім, керісінше, соғысуда және т.б. Бұл ақпаратты, біздің бағдарламамыздың күйін кейінірек деректерді қалпына келтіру және ойынды жалғастыру үшін қандай да бір жолмен сақтау керек. Сериализация және сериясыздандыру механизмдері дәл осы үшін пайдаланылады . Серияландыру - an object күйін byteтар тізбегіне сақтау процесі. Сериясыздандыру - бұл byteтардан нысанды қайта құру процесі. Кез келген Java нысаны byteтар тізбегіне түрлендіріледі. Ол не үшін? Бағдарламалардың өздігінен жоқ екенін біз бірнеше рет айтқанбыз. Көбінесе олар бір-бірімен өзара әрекеттеседі, мәліметтер алмасады және т.б. Ал бұл үшін byte пішімі ыңғайлы және тиімді. Біз, мысалы, сынып an objectісін SavedGame(сақталған ойынды) byteтар тізбегіне айналдыра аламыз, сол byteтарды желі арқылы басқа компьютерге тасымалдай аламыз, ал екінші компьютерде сол byteты қайтадан Java нысанына айналдыра аламыз! Есту қиын, солай ма? Бұл процесті ұйымдастыру оңай емес сияқты :/ Бақытымызға орай, жоқ! :) Java тілінде Серияланатын интерфейс сериялау процестеріне жауап береді . Бұл интерфейс өте қарапайым: оны пайдалану үшін бір әдісті қолданудың қажеті жоқ! Біздің сақтау ойыны класы келесідей болады:
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) +
               '}';
   }
}
Үш деректер жинағы аумақтар, экономика және дипломатия туралы ақпаратқа жауап береді және Серияланатын интерфейс Java құрылғысына: « бәрі жақсы, егер бірдеңе болса, осы сыныптың нысандарын сериялауға болады » дейді. Ешбір әдістері жоқ интерфейс біртүрлі көрінеді:/ Бұл не үшін қажет? Бұл сұрақтың жауабы жоғарыда: тек Java машинасына қажетті ақпаратты беру үшін. Алдыңғы лекциялардың бірінде біз маркер интерфейстерін қысқаша атап өттік. Бұл біздің сабақтарымызды болашақта Java машинасына пайдалы болатын қосымша ақпаратпен белгілейтін арнайы ақпараттық интерфейстер. Оларда енгізуді қажет ететін әдістер жоқ. Сонымен, Serializable - осындай интерфейстердің бірі. private static final long serialVersionUIDТағы бір маңызды мәселе: біз сыныпта анықтаған айнымалы . Ол не үшін қажет? Бұл өріс серияланған сыныптың бірегей нұсқа идентификаторын қамтиды . Serializable интерфейсін жүзеге асыратын кез келген сыныпта нұсқа идентификаторы бар. Ол сыныптың мазмұны бойынша есептеледі - өрістер, декларация тәртібі, әдістер. Ал егер біз сыныптағы өріс түрін және/немесе өрістер санын өзгертсек, нұсқа идентификаторы бірден өзгереді. serialVersionUID де класс серияланған кезде жазылады. Сериядан шығаруға, яғни byteтар жиынынан an objectіні қалпына келтіруге тырысқанда, мән бағдарламамыздағы класс serialVersionUIDмәнімен салыстырылады . serialVersionUIDМәндер сәйкес келмесе, java.io.InvalidClassException жіберіледі. Мұның мысалын төменде көреміз. Мұндай жағдайларды болдырмау үшін біз бұл нұсқа идентификаторын сынып үшін қолмен орнатамыз. Біздің жағдайда ол жай ғана 1-ге тең болады (сіз өзіңізге ұнайтын кез келген басқа санды ауыстыра аласыз). 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
       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();
   }
}
Көріп отырғаныңыздай, біз 2 ағын құрдық - FileOutputStreamжәне ObjectOutputStream. Олардың біріншісі деректерді файлға жаза алады, ал екіншісі an objectілерді byteтарға түрлендіре алады. Ұқсас «ішілген» конструкцияларды бұрыннан көрдіңіз, мысалы, new BufferedReader(new InputStreamReader(...))алдыңғы лекцияларда, сондықтан олар сізді қорқытпауы керек :) Екі ағынның осындай «тізбегін» жасау арқылы біз екі тапсырманы орындаймыз: an objectіні SavedGamebyteтар жинағына айналдырамыз. әдісін пайдаланып файлға сақтаңыз writeObject(). Айтпақшы, біз не алғанымызды тексермедік! Файлды қарау уақыты келді! *Ескерту: файлды алдын ала жасау қажет емес. Егер мұндай атаумен файл жоқ болса, ол автоматты түрде жасалады* Міне, оның мазмұны! ¬н sr SavedGame [ diplomatacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТVзй{G xp t pР¤СЂР°РЅС†РІРѕСЋРµС РЃСЃР˜РµРЃС˃Р ° РЅРёСЏ Р·Р°Ряла позицию нейтралитетаuq ~ t "РЈ Р˜СЃРїР°РЅРёРёР°Р1РёРёР10» РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Р˜СЃРїР°РЅР˜СЃРїР°РЅР˜СЃРїР°РЅРˠРёРёР %ЂииР6РЈ Франции Ј Р РѕСЃСЃРёРё 10 РїСЂРѕРІРёРЅС †РёР№t &РЈ ФранцРеРё 8 проввинций Ой :( Біздің бағдарлама жұмыс істемеген сияқты :( Шындығында, ол жұмыс істеді. Біз файлға нақты бір byte пен byteтар жинағын тасымалдағанымыз есіңізде болсын. жай ғана нысан немесе мәтін емес пе? Ал, бұл жиын осылай көрінеді :) Бұл біздің сақталған ойынымыз! Егер біз бастапқы нысанымызды қалпына келтіргіміз келсе, яғни ойынды тоқтаған жерімізден жүктеп алып, жалғастырғымыз келсе, бізге кері процесс , сериядан шығару ... Біздің жағдайда ол келесідей болады:
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);
   }
}
Міне, нәтиже! SavedGame{territoriesInfo=[Испанияда 6 провинция, Ресейде 10 провинция, Францияда 8 провинция бар], resourcesInfo=[Испанияда 100 алтын, Ресейде 80 алтын, Францияда 90 алтын], diplomatacyInfo=[Франция Ресеймен соғысуда, Испания бейтараптық позициясын иеленді]} Керемет! Біз алдымен ойынның күйін файлға сақтап, содан кейін оны файлдан қалпына келтірдік. Енді дәл солай істеуге тырысайық, бірақ SavedGameбіздің сыныптан нұсқа идентификаторын алып тастаңыз. Біз екі сыныпты да қайта жазбаймыз, олардағы code бірдей болады, біз жай ғана SavedGameалып тастаймыз private static final long serialVersionUID. Сериализациядан кейінгі нысанымыз: ¬н sr SavedGameі€MіuОm‰ [ diplomatacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТВзй{G xp t pФраняСанѽц ‚ СЃ Россией, Р˜СЃРїР°РЅРЏР·Р°РЅСЏР»Р° RїРѕР·РёС†РёСЋ РЅРμйтралитралитралатР˅"Р°РˈR˜t ёРё 100 Р · олотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЃё Р˅ t &СЃёР˅ їСЂРѕРІРёРЅС† РёР№t %РЈ Р Р РѕСЃСЃРёРё 10 провнинцийt &РЈ Франции тырысқанда 8 РРРёРЅС оны сериализациялаңыз, осылай болды: InvalidClassException: жергілікті сынып үйлесімсіз: stream classdesc serialVersionUID = - 196410440475012755, жергілікті сынып serialVersionUID = -6675950253085108747 Бұл жоғарыда айтылған ерекшелік. Бұл туралы толығырақ біздің студенттердің бірінің мақаласынан оқи аласыз . Айтпақшы, біз бір маңызды нәрсені жіберіп алғанбыз. Жолдар мен примитивтер екені анық. оңай серияланады: Java-да, әрине, кейбіреулері бар, бірақ бұл үшін кіріктірілген механизмдер бар. Бірақ егер біздің serializable-сыныпта қарабайыр емес, басқа нысандарға сілтеме ретінде көрсетілген өрістер болса ше? Мысалы , біздің сыныппен жұмыс істеу TerritoriesInfoүшін бөлек сыныптар құрайық . 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 + '\'' +
               '}';
   }
}
Бірақ қазір бізде сұрақ бар: егер біз өзгертілген сыныпты сериализациялағымыз келсе, осы сыныптардың барлығы сериялық болуы керек пе 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 +
               '}';
   }
}
Ал, мұны іс жүзінде тексеріп көрейік! Барлығын қазірге қалдырып, нысанды сериялауға тырысайық 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();
   }
}
Нәтиже: java.io.NotSerializableException "main" ағынындағы ерекшелік: DiplomacyInfo орындалмады! Шын мәнінде, бұл біздің сұрағымызға жауап. Нысанды сериялау кезінде, оның айнымалы даналарында сілтеме жасайтын барлық нысандар серияланады. Және бұл нысандар үшінші нысандарға да сілтеме жасаса, олар да серияланады. Және т.б. ad infinitum. Бұл тізбектегі барлық сыныптар Серияланатын болуы керек, әйтпесе олар серияланбайды және ерекшелік жойылады. Бұл, айтпақшы, болашақта қиындықтар тудыруы мүмкін. Мысалы, сериялау кезінде сыныптың бір бөлігі қажет болмаса, не істеуіміз керек? Немесе, мысалы, TerritoryInfoбіз қандай да бір кітапхананың бөлігі ретінде бағдарламамыздағы сыныпты мұра еттік. Дегенмен, ол сериялауға жатпайды, сондықтан оны өзгерте алмаймыз. Біз TerritoryInfoсыныпқа өрісті қоса алмаймыз , өйткені бұл кезде бүкіл сынып серияланbyteын болады! Мәселе:/ Осы түрдегі мәселелер Java тілінде кілт сөзі арқылы шешіледі . Бұл кілт сөзді сынып өрісіне қоссаңыз, бұл өрістің мәні серияланбайды. Біздің сыныптың өрістерінің бірін жасауға тырысайық , содан кейін біз бір нысанды сериялаймыз және қалпына келтіреміз. SavedGameSavedGameJava тіліндегі сериялау және сериядан шығару - 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();


   }
}
Міне, нәтиже: SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='Испанияда 100 алтын, Ресейде 80 алтын, Францияда 90 алтын'}, diplomatacyInfo=DiplomacyInfo{info='Франция Ресеймен соғысуда, Испания бейтараптық позициясын ұстанды'}}transient Бұл ретте біз -өріске қандай мән беріледі деген сұраққа жауап алдық . Оған әдепкі мән тағайындалады. Нысандарға қатысты бұл null. Сериализация туралы осы тамаша мақаланы бос уақытта оқи аласыз . Ол сондай-ақ интерфейс туралы айтады Externalizable, ол туралы келесі дәрісте айтатын боламыз. Сонымен қатар, бұл тақырып бойынша «Бірінші Java» кітабында тарау бар, оған назар аударыңыз :)
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION