CodeGym /Blog Java /rawak /Pensirian dan penyahserikatan di Jawa
John Squirrels
Tahap
San Francisco

Pensirian dan penyahserikatan di Jawa

Diterbitkan dalam kumpulan
Hai! Dalam pelajaran hari ini, kita akan bercakap tentang pensirilan dan penyahserikan di Jawa. Kita akan mulakan dengan contoh mudah. Bayangkan anda seorang pembangun permainan komputer. Jika anda dibesarkan pada tahun 90-an dan mengingati konsol permainan pada era itu, anda mungkin tahu bahawa mereka tidak mempunyai sesuatu yang kami anggap remeh hari ini — keupayaan untuk menyimpan dan memuatkan permainan :) Jika tidak, bayangkan itu!Pensirian dan penyahserikatan di Jawa - 1Saya takut bahawa permainan tanpa kebolehan ini hari ini akan ditakdirkan! Bagaimanapun, apakah maksud 'simpan' dan 'muatkan' permainan? Nah, kami memahami maksud setiap hari: kami mahu meneruskan permainan dari tempat kami berhenti. Untuk melakukan ini, kami mencipta 'titik semak' tertentu yang kami gunakan kemudian untuk memuatkan permainan. Tetapi apakah maksudnya kepada pengaturcara dan bukannya pemain permainan kasual? Jawapannya mudah: kami menyimpan keadaan program kami. Katakan anda bermain Sepanyol dalam permainan strategi. Permainan anda mempunyai keadaan: wilayah mana yang semua orang ada, berapa banyak sumber yang semua orang ada, apa pakatan yang wujud dan dengan siapa, siapa yang berperang, dan sebagainya. Maklumat ini, keadaan program kami, entah bagaimana mesti disimpan untuk memulihkan data dan meneruskan permainan. Seperti yang berlaku, Serialisasi dalam Java ialah proses menyimpan keadaan objek sebagai urutan bait. Penyahserialisasian di Jawa ialah proses memulihkan objek daripada bait ini. Mana-mana objek Java boleh ditukar kepada jujukan bait. Mengapa kita memerlukan ini? Kami telah berulang kali mengatakan bahawa program tidak wujud dengan sendirinya. Selalunya, mereka berinteraksi antara satu sama lain, bertukar data, dsb. Format bait adalah mudah dan cekap untuk ini. Sebagai contoh, kami boleh menukar objek SavedGame kamikelas ke dalam urutan bait, pindahkan bait ini melalui rangkaian ke komputer lain, dan kemudian pada komputer lain tukarkan bait ini kembali menjadi objek Java! Kedengarannya sukar, ya? Nampaknya sukar untuk membuat ini semua berlaku: / Untungnya, itu tidak berlaku! :) Di Java, antara muka Serializable bertanggungjawab untuk proses bersiri. Antara muka ini sangat mudah: Anda tidak perlu melaksanakan satu kaedah untuk menggunakannya! Lihat betapa mudahnya kelas kami untuk menyimpan permainan:

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) +
               '}';
   }
}
Tiga tatasusunan bertanggungjawab untuk maklumat tentang wilayah, sumber dan diplomasi, dan antara muka Serializable memberitahu mesin Java: ' semuanya baik-baik saja jika objek kelas ini boleh bersiri '.' Antara muka tanpa satu antara muka kelihatan pelik :/ Mengapa perlu? Jawapan kepada soalan itu diberikan di atas: ia hanya diperlukan untuk memberikan maklumat yang diperlukan kepada mesin Java. Dalam pelajaran lepas, kami menyebut secara ringkas antara muka penanda. Ini adalah antara muka maklumat 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 anda laksanakan. Inilah Serializable — satu antara muka sedemikian. Berikut adalah satu lagi perkara penting: Mengapa kita memerlukanpembolehubah bersiriVersionUID panjang akhir statik peribadi yang kami takrifkan dalam kelas? Medan ini mengandungi pengecam versi unik bagi kelas bersiri. Setiap kelas yang melaksanakan antara muka Serializable mempunyai pengecam versi. Ia ditentukan berdasarkan kandungan kelas — medan dan susunan pengisytiharannya, dan kaedah serta perintah pengisytiharannya. Dan jika kami menukar jenis medan dan/atau bilangan medan dalam kelas kami, pengecam versi berubah serta-merta. SeriVersionUID juga ditulis apabila kelas disiri. Apabila kita cuba menyahsiri, iaitu memulihkan objek daripada jujukan bait, nilai serialVersionUID dibandingkan dengan nilai serialVersionUIDkelas dalam program kami. Jika nilai tidak sepadan, maka java.io.InvalidClassException akan dibuang. Kita akan melihat contoh ini di bawah. Untuk mengelakkan situasi sedemikian, kami hanya menetapkan pengecam versi untuk kelas kami secara manual. Dalam kes kami, ia hanya akan sama dengan 1 (anda boleh menggunakan mana-mana nombor lain yang anda suka). Nah, sudah tiba masanya untuk cuba mensirikan objek SavedGame kami dan 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[] 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();
   }
}
Seperti yang anda lihat, kami mencipta 2 aliran: FileOutputStream dan ObjectOutputStream . Yang pertama tahu cara menulis data ke fail, dan yang kedua menukar objek kepada bait. Anda telah pun melihat binaan bersarang yang serupa, contohnya, new BufferedReader(new InputStreamReader(...)) , dalam pelajaran sebelumnya, jadi mereka tidak seharusnya menakutkan anda :) Dengan mencipta rantaian dua aliran ini, kami melaksanakan kedua-dua tugas: kami menukar objek SavedGame ke dalam urutan bait dan menyimpannya ke fail menggunakan kaedah writeObject() . Dan, dengan cara itu, kami tidak melihat apa yang kami dapat! Sudah tiba masanya untuk melihat fail! *Nota: anda tidak perlu membuat fail terlebih dahulu. Jika fail dengan nama yang dinyatakan tidak wujud, maka ia akan dibuat secara automatik* Dan berikut ialah kandungannya: ¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourceInfoq ~ [ territoryInfoq ~ xpur [Ljava.lang. Rentetan;¬ТVзй{G xp t pФранция РІТТVзй{G xp t pФранция воюет СЃ Россией, Р˜СЃРёСЏ РІРсюет СЃ Р Рсссией, СЃРёСЏ AS$$$$$$$$$$$$$$$$$$ етаuq ~ t "US$$$$ 100$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Рё 90 Р·РѕР »Perintah uq ~ t & РЈ Р˜СЃРїР°РЅРёРё 6 провинцийt %РЈ Р РсСЃСЃРёРёРё 10 №t &РЈ Франции 8 провинций Oh, oh :( Nampaknya program kami tidak berfungsi : ( Sebenarnya, ia berfungsi. Anda ingat bahawa kami menghantar urutan bait, dan bukan sekadar objek atau teks, ke fail? Nah, ini adalah urutan bait ini kelihatan seperti :) Ia adalah permainan kami yang disimpan! Jika kami ingin memulihkan objek asal kami, iaitu mulakan dan teruskan permainan di tempat kami berhenti, maka kami memerlukan proses terbalik: penyahserikatan. Beginilah rupa bagi 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{territoryInfo=[Sepanyol ada 6 wilayah, Rusia ada 10 wilayah, Perancis ada 8 wilayah], resourceInfo=[Sepanyol ada 100 emas, Rusia ada 80 emas, Perancis ada 90 emas], diplomasiInfo=[Perancis berperang dengan Rusia, Sepanyol telah mengambil kedudukan neutral]} Cemerlang! Kami berjaya menyimpan dahulu keadaan permainan kami ke fail, dan kemudian memulihkannya daripada fail. Sekarang mari kita cuba melakukan perkara yang sama, tetapi kami akan mengalih keluar pengecam versi daripada kelas SavedGame kami . Kami tidak akan menulis semula kedua-dua kelas kami. Kod mereka akan sama. Kami hanya akan mengalih keluar serialVersionUID panjang statik peribadi daripada kelas SavedGame . Inilah objek kami selepas bersiri: ¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourceInfoq ~ [ territoryInfoq ~ xpur [Ljava.lang.String;¬ТVзй{G xp t pФранциСЗСЗРА ЃСЃРёРµР№, спания аняла Русский просмотреть ё 100 золотР°t РЈ РЗСЃСЃЃРёРё 80 Арурский Русский t ! ЂРѕРІРёРЅС†РёР№t %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 првинцийt &РЈ Франции 8 првинцийt &РЈ Франции 8 првинцийt &РЈ Франции 8 РїСЂРѕРІР‖РЅs apabila kita cuba lihat apa yang berlaku : InvalidClassException: kelas tempatan tidak serasi: stream classdesc serialVersionUID = -196410440475012755, kelas tempatan serialVersionUID = -6675950253085108747 By the way, kami terlepas sesuatu yang penting. Jelas sekali, rentetan dan primitif bersiri dengan mudah: Java sudah tentu mempunyai beberapa mekanisme terbina dalam untuk ini. Tetapi bagaimana jika kelas boleh bersiri kami mempunyai medan yang bukan primitif, sebaliknya merujuk kepada objek lain? Sebagai contoh, mari buat kelas TerritoryInfo , ResourceInfo dan DiplomacyInfo yang berasingan untuk bekerja dengan kelas SavedGame kami .

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 + '\'' +
               '}';
   }
}
Dan kini kita menghadapi satu soalan: Mestikah semua kelas ini Boleh Bersiri jika kita mahu membuat siri kelas SavedGame kita ?

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 +
               '}';
   }
}
Baiklah, kalau begitu! Jom uji! Buat masa ini, kami akan membiarkan segala-galanya seperti sedia ada dan cuba mensirikan 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
       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();
   }
}
Keputusan: Pengecualian dalam utas "utama" java.io.NotSerializableException: DiplomacyInfo Ia tidak berjaya! Jadi, inilah jawapan kepada soalan kami. Apabila objek bersiri, semua objek yang dirujuk oleh pembolehubah contohnya disiri. Dan jika objek tersebut juga merujuk kepada objek lain, maka ia juga bersiri. Dan seterusnya selama-lamanya. Semua kelas dalam rantaian ini mestilah Serializable , jika tidak, mustahil untuk mensirikannya dan pengecualian akan dibuang. Dengan cara ini, ini boleh menimbulkan masalah di jalan raya. Sebagai contoh, apakah yang perlu kita lakukan jika kita tidak memerlukan sebahagian daripada kelas semasa bersiri? Atau bagaimana jika kami mendapat kelas TerritoryInfo kami 'melalui warisan' sebagai sebahagian daripada perpustakaan? Dan andaikan lagi bahawa ia adalah'dan, oleh itu, kita tidak boleh mengubahnya. Ini bermakna kami tidak boleh menambah medan TerritoryInfo pada kelas SavedGame kami , kerana keseluruhan kelas SavedGame akan menjadi tidak boleh disiri! Itu masalah: / Pensirian dan penyahserikatan di Jawa - 2Di Jawa, masalah seperti ini diselesaikan oleh kata kunci sementara . Jika anda menambahkan kata kunci ini pada medan kelas anda, maka medan itu tidak akan bersiri. Mari cuba jadikan salah satu medan kelas SavedGame kami sementara , dan kemudian kami akan bersiri dan memulihkan satu objek.

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


   }
}
Dan inilah hasilnya: SavedGame{territoryInfo=null, resourceInfo=ResourceInfo{info='Sepanyol ada 100 emas, Rusia ada 80 emas, Perancis ada 90 emas'}, diplomacyInfo=DiplomacyInfo{info='Perancis berperang dengan Rusia, Sepanyol telah mengambil kedudukan neutral'}} Yang berkata, kami mendapat jawapan kepada soalan tentang nilai yang akan diberikan kepada medan sementara . Ia diberikan nilai lalai. Untuk objek, ini adalah null . Anda boleh membaca bab yang sangat baik mengenai topik ini dalam buku 'Head-First Java', Beri perhatian kepadanya :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION