JavaRush /Blog Java /Random-MS /Serialisasi dan Deserialisasi di Jawa

Serialisasi dan Deserialisasi di Jawa

Diterbitkan dalam kumpulan
hello! Dalam kuliah hari ini kita akan bercakap tentang bersiri dan penyahserikatan di Jawa. Mari kita mulakan dengan contoh mudah. Katakan anda adalah pencipta permainan komputer. Jika anda dibesarkan pada tahun 90-an dan ingat konsol permainan pada masa itu, anda mungkin tahu bahawa mereka kekurangan sesuatu yang jelas hari ini - menyimpan dan memuatkan permainan :) Jika tidak... bayangkan! Saya takut hari ini permainan tanpa peluang sedemikian akan ditakdirkan untuk gagal! Dan, sebenarnya, apakah "menyimpan" dan "memuatkan" permainan? Nah, dalam erti kata biasa, kami faham apa itu: kami mahu meneruskan permainan dari tempat kami berhenti kali terakhir. Untuk melakukan ini, kami mencipta sejenis "titik pemeriksaan", yang kemudian kami gunakan untuk memuatkan permainan. Tetapi apakah maksud ini, bukan dalam erti kata sehari-hari, tetapi dalam erti kata "pengaturcara"? Jawapannya mudah: kami menyimpan keadaan program kami. Katakan anda sedang bermain permainan strategi untuk Sepanyol. Permainan anda mempunyai keadaan: siapa yang memiliki wilayah apa, siapa yang mempunyai berapa banyak sumber, siapa yang bersekutu dengan siapa, dan siapa, sebaliknya, berperang, dan sebagainya. Maklumat ini, keadaan program kami, mesti disimpan entah bagaimana untuk memulihkan data kemudian dan meneruskan permainan. Inilah yang sebenarnya digunakan oleh mekanisme penyirian dan penyahserikan . Serialisasi ialah proses menyimpan keadaan objek ke dalam urutan bait. Deserialisasi ialah proses membina semula objek daripada bait ini. Mana-mana objek Java ditukar kepada urutan bait. Untuk apa itu? Kami telah mengatakan lebih daripada sekali bahawa program tidak wujud dengan sendirinya. Selalunya mereka berinteraksi antara satu sama lain, bertukar data, dsb. Dan format bait adalah mudah dan cekap untuk ini. Kita boleh, sebagai contoh, menukar objek kelas kita SavedGame(permainan yang disimpan) ke dalam urutan bait, memindahkan bait tersebut melalui rangkaian ke komputer lain dan pada komputer kedua menukar bait tersebut kembali menjadi objek Java! Susah nak dengar kan? Nampaknya, mengatur proses ini tidak akan mudah :/ Nasib baik, tidak! :) Di Java, antara muka Serializable bertanggungjawab untuk proses bersiri . Antara muka ini sangat mudah: anda tidak perlu melaksanakan satu kaedah untuk menggunakannya! Beginilah rupa kelas permainan simpan kami:
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) +
               '}';
   }
}
Tiga set data bertanggungjawab untuk maklumat tentang wilayah, ekonomi dan diplomasi, dan antara muka Serializable memberitahu mesin Java: " semuanya ok, jika ada, objek kelas ini boleh bersiri ." Antara muka yang tidak mempunyai sebarang kaedah kelihatan pelik :/ Mengapa ia diperlukan? Jawapan kepada soalan ini adalah di atas: hanya untuk memberikan maklumat yang diperlukan kepada mesin Java. Dalam salah satu kuliah sebelum ini, kami menyebut secara ringkas antara muka penanda. Ini adalah antara muka bermaklumat khas yang hanya menandakan kelas kami dengan maklumat tambahan yang akan berguna kepada mesin Java pada masa hadapan. Mereka tidak mempunyai sebarang kaedah yang perlu dilaksanakan. Jadi, Serializable adalah salah satu antara muka sedemikian. Satu lagi perkara penting: pembolehubah private static final long serialVersionUIDyang kami takrifkan dalam kelas. Mengapa ia diperlukan? Medan ini mengandungi pengecam versi unik kelas bersiri . Mana-mana kelas yang melaksanakan antara muka Serializable mempunyai pengecam versi. Ia dikira berdasarkan kandungan kelas - medan, perintah pengisytiharan, kaedah. Dan jika kami menukar jenis medan dan/atau bilangan medan dalam kelas kami, pengecam versi akan berubah serta-merta. serialVersionUID juga ditulis apabila kelas bersiri. Apabila kita cuba menyahsiri, iaitu memulihkan objek daripada set bait, nilainya serialVersionUIDdibandingkan dengan nilai serialVersionUIDkelas dalam program kami. Jika nilai tidak sepadan, java.io.InvalidClassException akan dibuang. Kita akan melihat contoh ini di bawah. Untuk mengelakkan situasi sedemikian, kami hanya menetapkan ID versi ini secara manual untuk kelas kami. Dalam kes kami, ia hanya akan sama dengan 1 (anda boleh menggantikan mana-mana nombor lain yang anda suka). Nah, sudah tiba masanya untuk mencuba bersiri objek kami SavedGamedan lihat apa yang berlaku!
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();
   }
}
Seperti yang anda lihat, kami telah mencipta 2 utas - FileOutputStreamdan ObjectOutputStream. Yang pertama boleh menulis data ke fail, dan yang kedua boleh menukar objek menjadi bait. Anda telah pun melihat pembinaan "bersarang" yang serupa, contohnya, new BufferedReader(new InputStreamReader(...))dalam kuliah sebelumnya, jadi mereka tidak seharusnya menakutkan anda :) Dengan mencipta "rantai" dua utas, kami melaksanakan kedua-dua tugas: kami menukar objek SavedGamemenjadi satu set bait dan simpan ke fail menggunakan kaedah writeObject(). Dan, dengan cara ini, kami tidak menyemak pun apa yang kami dapat! Sudah tiba masanya untuk melihat fail! *Nota: fail tidak perlu dibuat terlebih dahulu. Jika fail dengan nama itu tidak wujud, ia akan dibuat secara automatik* Dan berikut ialah kandungannya! ¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТVзй{G xp t pФранцвоюетSЃСЗетРСЃСР Р, РСЃСР Р, їР° РЅРёСЏ заЏла РїРусР° ицию РЅРяŠтаt Р J R 80 Русский Русский t ! РЅС †РёР№t %РЈ Р РѕСЃСЃРёРё 10 РїСЂРѕРІРёРЅС †РёР№t &РЈ ФранцРеРё 8 проввинций Oops :( Nampaknya program kami tidak berfungsi :( Sebenarnya, ia berfungsi. Anda ingat bahawa kami telah memindahkan tepat ke satu set bukan sekadar objek atau teks? Beginilah rupa set ini :) Ini adalah permainan kami yang disimpan! Jika kami ingin memulihkan objek asal kami, iaitu memuatkan dan meneruskan permainan dari tempat kami berhenti, kami memerlukan proses terbalik , penyahserialisasian ... Beginilah rupanya dalam kes kami:
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);
   }
}
Dan inilah hasilnya! SavedGame{territoriesInfo=[Sepanyol ada 6 wilayah, Rusia ada 10 wilayah, Perancis ada 8 wilayah], resourcesInfo=[Sepanyol ada 100 emas, Rusia ada 80 emas, Perancis ada 90 emas], diplomasiInfo=[Perancis sedang berperang dengan Rusia, Sepanyol telah menduduki kedudukan berkecuali]} Hebat! Kami berjaya menyimpan dahulu keadaan permainan kami ke fail, dan kemudian memulihkannya daripada fail. Sekarang mari cuba lakukan perkara yang sama, tetapi alih keluar SavedGamepengecam versi daripada kelas kami. Kami tidak akan menulis semula kedua-dua kelas kami, kod di dalamnya akan sama, kami hanya akan SavedGamemengalih keluar private static final long serialVersionUID. Berikut ialah objek kami selepas bersiri: ¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТВзй{G xp t pФранССКРї ‚ СЃ РРсссией, спанЏзаняла RїРусРусский ~ "РЈ Р˜СЃРї ании 100 Р · Русский Русский Русский 80 Руский ёРё 6 R 10 ѕРІРІРёРЅС†РёРЅА Dan apabila cuba menyahsirikannya, inilah yang berlaku: InvalidClassException: local class incompatible : stream classdesc serialVersionUID = - 196410440475012755, kelas tempatan serialVersionUID = -6675950253085108747 Ini adalah pengecualian yang sama yang dinyatakan di atas. Anda boleh membaca lebih lanjut mengenai perkara ini dalam artikel salah seorang pelajar kami. Sebenarnya, kami terlepas satu perkara penting. Jelas sekali bahawa rentetan dan primitif mudah bersiri: Java sudah tentu mempunyai beberapa- maka terdapat mekanisme terbina dalam untuk ini. Tetapi bagaimana jika -class kami serializablemempunyai medan yang dinyatakan bukan sebagai primitif, tetapi sebagai rujukan kepada objek lain? Mari, sebagai contoh, buat kelas berasingan TerritoriesInfountuk berfungsi dengan kelas kami . 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 + '\'' +
               '}';
   }
}
Tetapi sekarang kita mempunyai soalan: patutkah semua kelas ini Boleh Bersiri jika kita mahu mensirikan kelas yang diubah 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 +
               '}';
   }
}
Baiklah, mari semak ini dalam amalan! Mari kita biarkan segala-galanya seperti sekarang dan cuba bersiri objek 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();
   }
}
Keputusan: Pengecualian dalam utas "utama" java.io.NotSerializableException: DiplomacyInfo Gagal! Sebenarnya, inilah jawapan kepada soalan kami. Apabila anda mensiri objek, semua objek yang dirujuk dalam pembolehubah contohnya adalah bersiri. Dan jika objek tersebut juga merujuk kepada objek ketiga, ia juga bersiri. Dan seterusnya ad infinitum. Semua kelas dalam rantaian ini mestilah Boleh Bersiri, jika tidak, mereka tidak akan boleh bersiri dan pengecualian akan dibuang. Ini, dengan cara, boleh menimbulkan masalah pada masa hadapan. Apakah yang perlu kita lakukan, contohnya, jika kita tidak memerlukan sebahagian daripada kelas semasa bersiri? Atau, sebagai contoh, TerritoryInfokami mewarisi kelas dalam program kami sebagai sebahagian daripada beberapa perpustakaan. Walau bagaimanapun, ia tidak boleh bersiri, dan, oleh itu, kami tidak boleh mengubahnya. Ternyata kami tidak boleh menambah medan TerritoryInfopada kelas kami , kerana keseluruhan kelas akan menjadi tidak boleh bersiri! Masalah:/ Masalah seperti ini diselesaikan dalam Java menggunakan kata kunci . Jika anda menambah kata kunci ini pada medan kelas, nilai medan ini tidak akan bersiri. Mari cuba buat salah satu medan kelas kami , selepas itu kami akan bersiri dan memulihkan satu objek. SavedGameSavedGamePensirilan dan Penyahserikatan di Jawa - 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();


   }
}
Dan inilah hasilnya: SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='Sepanyol ada 100 emas, Rusia ada 80 emas, Perancis ada 90 emas'}, diplomacyInfo=DiplomacyInfo{info='Perancis berperang dengan Rusia, Sepanyol telah mengambil sikap berkecuali'}} Pada masa yang sama, kami menerima jawapan kepada soalan tentang nilai yang akan diberikan transientkepada medan -. Ia diberikan nilai lalai. Dalam kes objek ini adalah null. Pada masa lapang anda, anda boleh membaca artikel hebat ini tentang penyiaran . Ia juga bercakap tentang antara muka Externalizable, yang akan kita bincangkan dalam kuliah seterusnya. Di samping itu, terdapat satu bab mengenai topik ini dalam buku "Head-First Java", perhatikannya :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION