CodeGym /Java Blog /Acak /Serialisasi dan deserialisasi di Jawa
John Squirrels
Level 41
San Francisco

Serialisasi dan deserialisasi di Jawa

Dipublikasikan di grup Acak
Hai! Dalam pelajaran hari ini, kita akan berbicara tentang serialisasi dan deserialisasi di Java. Kita akan mulai dengan contoh sederhana. Bayangkan Anda adalah pengembang game komputer. Jika Anda tumbuh di tahun 90-an dan mengingat konsol game pada masa itu, Anda mungkin tahu bahwa mereka kekurangan sesuatu yang kita anggap remeh hari ini - kemampuan untuk menyimpan dan memuat game :) Jika tidak, bayangkan itu!Serialisasi dan deserialisasi di Jawa - 1Saya khawatir game tanpa kemampuan ini hari ini akan hancur! Ngomong-ngomong, apa artinya 'menyimpan' dan 'memuat' game? Nah, kami memahami arti sehari-hari: kami ingin melanjutkan permainan dari tempat kami tinggalkan. Untuk melakukan ini, kami membuat 'titik pemeriksaan' tertentu yang kami gunakan nanti untuk memuat game. Tapi apa artinya bagi seorang programmer daripada seorang gamer biasa? Jawabannya sederhana: kami menyimpan status program kami. Katakanlah Anda memainkan Spanyol dalam game strategi. Gim Anda memiliki status: wilayah mana yang dimiliki setiap orang, berapa banyak sumber daya yang dimiliki setiap orang, aliansi apa yang ada dan dengan siapa, siapa yang berperang, dan seterusnya. Informasi ini, status program kami, entah bagaimana harus disimpan untuk memulihkan data dan melanjutkan permainan. Seperti yang terjadi, Serialisasi di Java adalah proses menyimpan status objek sebagai urutan byte. Deserialisasi di Jawa adalah proses memulihkan objek dari byte ini. Objek Java apa pun dapat dikonversi menjadi urutan byte. Kenapa kita perlu ini? Kami berulang kali mengatakan bahwa program tidak ada dengan sendirinya. Paling sering, mereka berinteraksi satu sama lain, bertukar data, dll. Format byte nyaman dan efisien untuk ini. Misalnya, kita dapat mengonversi objek dari SavedGame kitakelas menjadi urutan byte, transfer byte ini melalui jaringan ke komputer lain, dan kemudian di komputer lain ubah byte ini kembali menjadi objek Java! Kedengarannya sulit, ya? Sepertinya akan sulit untuk mewujudkan semua ini: / Untungnya, bukan itu masalahnya! :) Di Java, antarmuka Serializable bertanggung jawab atas proses serialisasi. Antarmuka ini sangat sederhana: Anda tidak perlu menerapkan satu metode pun untuk menggunakannya! Lihatlah betapa sederhananya kelas kami untuk menyimpan game:

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 larik bertanggung jawab atas informasi tentang wilayah, sumber daya, dan diplomasi, dan antarmuka Serializable memberi tahu mesin Java: ' semuanya baik-baik saja jika objek dari kelas ini dapat diserialisasi '.' Antarmuka tanpa antarmuka tunggal terlihat aneh :/ Mengapa perlu? Jawaban atas pertanyaan itu diberikan di atas: diperlukan hanya untuk memberikan informasi yang diperlukan ke mesin Java. Dalam pelajaran sebelumnya, kami secara singkat menyebutkan antarmuka penanda. Ini adalah antarmuka informasi khusus yang menandai kelas kami dengan informasi tambahan yang akan berguna untuk mesin Java di masa mendatang. Mereka tidak memiliki metode apa pun yang harus Anda terapkan. Inilah Serializable - salah satu antarmuka tersebut. Inilah poin penting lainnya: Mengapa kita membutuhkanprivate variabel serialVersionUID panjang akhir statis yang kita definisikan di kelas? Bidang ini berisi pengidentifikasi versi unik dari kelas serial. Setiap kelas yang mengimplementasikan antarmuka Serializable memiliki pengidentifikasi versi. Itu ditentukan berdasarkan konten kelas — bidang dan urutan deklarasinya, serta metode dan urutan deklarasinya. Dan jika kita mengubah jenis bidang dan/atau jumlah bidang di kelas kita, pengidentifikasi versi akan langsung berubah. serialVersionUID juga ditulis saat kelas diserialkan. Ketika kita mencoba untuk deserialize, yaitu mengembalikan objek dari urutan byte, nilai serialVersionUID dibandingkan dengan nilai serialVersionUIDkelas dalam program kami. Jika nilainya tidak cocok, maka java.io.InvalidClassException akan dilempar. Kita akan melihat contohnya di bawah ini. Untuk menghindari situasi seperti itu, kami cukup mengatur pengidentifikasi versi untuk kelas kami secara manual. Dalam kasus kami, itu hanya akan sama dengan 1 (Anda dapat menggunakan nomor lain yang Anda suka). Nah, saatnya mencoba membuat serial objek SavedGame kita dan lihat apa yang terjadi!

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 membuat 2 aliran: FileOutputStream dan ObjectOutputStream . Yang pertama tahu cara menulis data ke file, dan yang kedua mengubah objek menjadi byte. Anda telah melihat konstruksi bersarang yang serupa, misalnya, new BufferedReader(new InputStreamReader(...)) , di pelajaran sebelumnya, jadi mereka seharusnya tidak membuat Anda takut :) Dengan membuat rantai dua aliran ini, kami melakukan kedua tugas: kami mengonversi objek SavedGame menjadi urutan byte dan menyimpannya ke file menggunakan metode writeObject() . Dan, omong-omong, kami bahkan tidak melihat apa yang kami dapatkan! Saatnya untuk melihat file! *Catatan: tidak perlu membuat file terlebih dahulu. Jika file dengan nama yang ditentukan tidak ada, maka akan dibuat secara otomatis* Dan berikut isinya: ¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourceInfoq ~ [teritoriInfoq ~ xpur [Ljava.lang. String;¬ТVзй{G xp t pФранция воюет СЃ Россией, Р˜СЃРїР°РЅ РёСЏ заняла позицию нейтралит етаuq ~ t "РЈ Р˜СЃРїР°РЅРёРё 100 золотаt РЈ Р РѕСЃСЃРёРё 80 Р·РѕР» отаt !РЈ Франции 90 Р·РѕР »РѕС‚Р°uq ~ t &РЈ Р˜СЃРїР°РЅРёРё 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 Nilai Oh, oh :( Sepertinya program kami tidak berfungsi : ( Sebenarnya, itu berhasil. Anda ingat bahwa kami mengirim urutan byte, dan bukan hanya objek atau teks, ke file? Nah, ini urutan byte ini sepertinya :) Ini adalah permainan kami yang disimpan! Jika kami ingin memulihkan objek asli kami, yaitu memulai dan melanjutkan permainan di mana kami tinggalkan, maka kami memerlukan proses sebaliknya: deserialisasi. Seperti inilah tampilannya 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=[Spanyol memiliki 6 provinsi, Rusia memiliki 10 provinsi, Prancis memiliki 8 provinsi], resourceInfo=[Spanyol memiliki 100 emas, Rusia memiliki 80 emas, Prancis memiliki 90 emas], diplomacyInfo=[Prancis berperang dengan Rusia, Spanyol telah mengambil posisi netral]} Luar biasa! Kami berhasil menyimpan status permainan kami terlebih dahulu ke sebuah file, dan kemudian memulihkannya dari file tersebut. Sekarang mari kita coba melakukan hal yang sama, tetapi kita akan menghapus pengidentifikasi versi dari kelas SavedGame kita . Kami tidak akan menulis ulang kedua kelas kami. Kode mereka akan sama. Kami hanya akan menghapus private static final long serialVersionUID dari kelas SavedGame . Inilah objek kami setelah serialisasi: ¬н sr SavedGameі€MіuОm‰ [ diplomasiInfot [Ljava/lang/String;[ resourceInfoq ~ [teritoriInfoq ~ xpur [Ljava.lang.String;¬ТVзй{G xp t pФранция РІРѕСЋРµС ‚ СЃ Россией, Р˜СЃРїР°РЅРёСЏ заняла позицию РЅРµР№С‚СЂР°Р»РёС‚РµС ‚Р°uq ~ t "РЈ Р˜СЃРїР°РЅРёРё 100 золотР°t РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Р˜СЃРїР°РЅРёРё 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 РїСЂРѕРІР ёРЅС†РёР№ Tapi lihat apa yang terjadi saat kami mencoba melakukan deserialize: InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747 Ngomong-ngomong, kami melewatkan sesuatu yang penting. Jelas, string dan primitif diserialkan dengan mudah: Java pasti memiliki beberapa mekanisme bawaan untuk ini. Tapi bagaimana jika kelas serializable kita memiliki bidang yang bukan primitif, melainkan referensi ke objek lain? Sebagai contoh, mari buat kelas TerritoryInfo , ResourceInfo dan DiplomacyInfo terpisah untuk bekerja dengan kelas SavedGame kita .

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 sekarang kita menghadapi pertanyaan: Apakah semua kelas ini harus Serializable jika kita ingin membuat serial 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 +
               '}';
   }
}
Baik-baik saja maka! Mari kita mengujinya! Untuk saat ini, kami akan membiarkan semuanya apa adanya dan mencoba membuat serial 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();
   }
}
Hasil: Pengecualian di utas "utama" java.io.NotSerializableException: DiplomacyInfo Tidak berhasil! Jadi, inilah jawaban untuk pertanyaan kami. Ketika sebuah objek diserialisasi, semua objek yang direferensikan oleh variabel instannya diserialisasi. Dan jika objek tersebut juga mereferensikan objek lain, maka objek tersebut juga akan diserialisasikan. Dan terus dan terus selamanya. Semua kelas dalam rantai ini harus Serializable , jika tidak maka tidak mungkin untuk membuat cerita bersambung dan pengecualian akan dilemparkan. Ngomong-ngomong, ini bisa menimbulkan masalah di jalan. Misalnya, apa yang harus kita lakukan jika kita tidak memerlukan bagian dari kelas selama serialisasi? Atau bagaimana jika kita mendapatkan kelas TerritoryInfo 'melalui warisan' sebagai bagian dari perpustakaan? Dan misalkan lebih jauh bahwa itu bukan 'dan, karenanya, kami tidak dapat mengubahnya. Itu berarti bahwa kita tidak dapat menambahkan bidang TerritoryInfo ke kelas SavedGame kita , karena seluruh kelas SavedGame akan menjadi tidak dapat di-serial! Itu masalah: / Serialisasi dan deserialisasi di Jawa - 2Di Jawa, masalah semacam ini diselesaikan dengan transient kata kunci. Jika Anda menambahkan kata kunci ini ke bidang kelas Anda, maka bidang itu tidak akan diserialisasi. Mari kita coba membuat salah satu bidang dari kelas SavedGame kita menjadi transient , lalu kita akan membuat cerita bersambung 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='Spanyol punya 100 emas, Rusia punya 80 emas, Prancis punya 90 emas'}, diplomacyInfo=DiplomacyInfo{info='Prancis berperang dengan Rusia, Spanyol telah mengambil posisi netral'}} Yang mengatakan, kami mendapat jawaban atas pertanyaan tentang nilai apa yang akan diberikan ke bidang transien . Ini diberi nilai default. Untuk objek, ini adalah null . Anda dapat membaca bab yang sangat bagus tentang topik ini di buku 'Head-First Java', Perhatikan itu :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION