JavaRush /Java Blog /Random-ID /Serialisasi dan Deserialisasi di Java

Serialisasi dan Deserialisasi di Java

Dipublikasikan di grup Random-ID
Halo! Pada kuliah hari ini kita akan membahas tentang serialisasi dan deserialisasi di Java. Mari kita mulai dengan contoh sederhana. Katakanlah Anda adalah pencipta permainan komputer. Jika Anda tumbuh di tahun 90an dan mengingat konsol game pada masa itu, Anda mungkin tahu bahwa konsol tersebut kekurangan sesuatu saat ini - menyimpan dan memuat game :) Jika tidak... bayangkan! Saya khawatir hari ini permainan tanpa peluang seperti itu akan gagal! Dan sebenarnya, apa yang dimaksud dengan “menyimpan” dan “memuat” sebuah game? Ya, dalam pengertian biasa, kami memahaminya: kami ingin melanjutkan permainan dari bagian terakhir yang kami tinggalkan. Untuk melakukan ini, kami membuat semacam “pos pemeriksaan”, yang kemudian kami gunakan untuk memuat game. Tapi apa artinya ini, bukan dalam pengertian sehari-hari, tapi dalam pengertian “programmer”? Jawabannya sederhana: kita menyimpan status program kita. Katakanlah Anda sedang memainkan permainan strategi untuk Spanyol. Permainan Anda memiliki status: siapa yang memiliki wilayah apa, siapa yang memiliki berapa banyak sumber daya, siapa yang bersekutu dengan siapa, dan siapa, sebaliknya, berperang, dan seterusnya. Informasi ini, status program kita, harus disimpan agar nanti dapat memulihkan data dan melanjutkan permainan. Inilah tepatnya mekanisme serialisasi dan deserialisasi yang digunakan . Serialisasi adalah proses menyimpan keadaan suatu objek ke dalam urutan byte. Deserialisasi adalah proses merekonstruksi suatu objek dari byte-byte ini. Setiap objek Java diubah menjadi urutan byte. Untuk apa? Kami telah mengatakan lebih dari sekali bahwa program tidak dapat berdiri sendiri. Paling sering mereka berinteraksi satu sama lain, bertukar data, dll. Dan format byte nyaman dan efisien untuk ini. Kita dapat, misalnya, mengubah objek kelas kita SavedGame(permainan yang disimpan) menjadi serangkaian byte, mentransfer byte tersebut melalui jaringan ke komputer lain, dan di komputer kedua mengubah byte tersebut kembali menjadi objek Java! Sulit untuk mendengarnya, bukan? Rupanya, mengatur proses ini tidak mudah :/ Untungnya, tidak! :) Di Java, antarmuka Serializable bertanggung jawab atas proses serialisasi . Antarmuka ini sangat sederhana: Anda tidak perlu menerapkan satu metode pun untuk menggunakannya! Seperti inilah tampilan kelas permainan simpanan kita:
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 kumpulan data bertanggung jawab atas informasi tentang wilayah, ekonomi, dan diplomasi, dan antarmuka Serializable memberi tahu mesin Java: " semuanya baik-baik saja, jika ada, objek kelas ini dapat diserialisasikan ." Antarmuka yang tidak memiliki metode apa pun terlihat aneh :/ Mengapa diperlukan? Jawaban atas pertanyaan ini ada di atas: hanya untuk memberikan informasi yang diperlukan ke mesin Java. Dalam salah satu kuliah sebelumnya, kami secara singkat menyebutkan antarmuka penanda. Ini adalah antarmuka informatif khusus yang sekadar menandai kelas kita dengan informasi tambahan yang akan berguna bagi mesin Java di masa mendatang. Mereka tidak memiliki metode apa pun yang perlu diterapkan. Jadi, Serializable adalah salah satu antarmuka tersebut. Poin penting lainnya: variabel private static final long serialVersionUIDyang kita definisikan di kelas. Mengapa itu diperlukan? Bidang ini berisi pengidentifikasi versi unik dari kelas serial . Setiap kelas yang mengimplementasikan antarmuka Serializable memiliki pengidentifikasi versi. Itu dihitung berdasarkan konten kelas - bidang, urutan deklarasi, metode. Dan jika kita mengubah tipe field dan/atau jumlah field di kelas kita, pengidentifikasi versi akan langsung berubah. serialVersionUID juga ditulis ketika kelas diserialkan. Ketika kita mencoba melakukan deserialize, yaitu mengembalikan suatu objek dari sekumpulan byte, nilainya serialVersionUIDdibandingkan dengan nilai serialVersionUIDkelas dalam program kita. Jika nilainya tidak cocok, java.io.InvalidClassException akan dilempar. Kita akan melihat contohnya di bawah. Untuk menghindari situasi seperti itu, kita cukup mengatur ID versi ini secara manual untuk kelas kita. Dalam kasus kami, angkanya sama dengan 1 (Anda dapat mengganti angka lain yang Anda suka). Nah, sekarang saatnya mencoba membuat serialisasi objek kita SavedGamedan 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[] 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 membuat 2 utas - FileOutputStreamdan ObjectOutputStream. Yang pertama bisa menulis data ke file, dan yang kedua bisa mengubah objek menjadi byte. Anda telah melihat konstruksi "bersarang" yang serupa, misalnya, new BufferedReader(new InputStreamReader(...))di kuliah sebelumnya, jadi itu tidak akan membuat Anda takut :) Dengan membuat "rantai" dua utas, kami melakukan kedua tugas: kami mengubah objek SavedGamemenjadi satu set byte dan simpan ke file menggunakan metode writeObject(). Dan omong-omong, kami bahkan tidak memeriksa apa yang kami dapatkan! Saatnya melihat filenya! *Catatan: file tidak perlu dibuat terlebih dahulu. Jika file dengan nama tersebut tidak ada, maka akan dibuat secara otomatis* Dan berikut isinya! ¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ teritorialInfoq ~ xpur [Ljava.lang.String;ТVзй{G xp t pФранцРвоюет SЃ R РѕСЃСЃР ёРµР№, Р˜С ЃРїР° РЅРёСЏ Р·Р°Ряла позицию нейтралитетаuq ~ t "РЈ Р ˜СЃРїР°РЅРёРё 100 золотаt Р J R UQ ~ t &РЈ Р˜ спании 6 РїСЂРѕРІРёРЅС †РёР№t %РЈ Р РѕСЃСЃРёРё 10 РїСЂРѕРІРёРЅ С †РёР№t &РЈ ФранцРеРё 8 проввинций Ups :( Tampaknya program kami tidak berfungsi :( Sebenarnya, berhasil. Anda ingat bahwa kami mentransfer satu set byte ke file, dan bukan sekedar objek atau teks? Nah, seperti inilah kumpulan ini :) Ini adalah permainan simpanan kita! Jika kita ingin mengembalikan objek asli kita, yaitu memuat dan melanjutkan permainan dari tempat terakhir kita tinggalkan, kita memerlukan proses terbalik , deserialisasi ... Seperti inilah tampilannya dalam kasus 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=[Spanyol punya 6 provinsi, Rusia punya 10 provinsi, Prancis punya 8 provinsi], resourcesInfo=[Spanyol punya 100 emas, Rusia punya 80 emas, Prancis punya 90 emas], diplomacyInfo=[Prancis berperang dengan Rusia, Spanyol telah menduduki posisi netral]} Hebat! 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 hapus SavedGamepengidentifikasi versi dari kelas kita. Kami tidak akan menulis ulang kedua kelas kami, kode di dalamnya akan sama, kami hanya akan SavedGamemenghapusnya private static final long serialVersionUID. Berikut objek kita setelah serialisasi: ¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ teritorialInfoq ~ xpur [Ljava.lang.String;ТВзй{G xp t pФранция РІРѕСЋ µС ‚ СЃ Россией, Р˜СЃРїР°РЅРЏР·Р°РЅСЏР»Р° RїРѕР·РёС†РёСЋ ней тралитетаuq ~ t "РЈ Р˜СЃРї ании 100 Р · олотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 Р·Рѕ лотаuq ~ t &РЈ Р˜СЃРїР°РЅРёРё 6 Р їСЂРѕРІРёРЅС† РёР№t %РЈ Р РѕСЃСЃРёРё 10 провнинцийt &РЈ Р ¤СЂР°РЅС†РёРё 8 проввинцинА Dan ketika mencoba melakukan deserialize, inilah yang terjadi: InvalidClassException: kelas lokal tidak kompatibel : stream classdesc serialVersionUID = - 196410440475012755, kelas lokal serialVersionUID = -6675950253085108747 Ini adalah pengecualian yang sama yang disebutkan di atas. Anda dapat membaca lebih lanjut tentang ini di artikel salah satu siswa kami. Ngomong-ngomong, kami melewatkan satu poin penting. Jelas bahwa string dan primitif mudah diserialkan: Java mungkin memiliki beberapa mekanisme bawaan untuk ini. Namun bagaimana jika serializablekelas -kita memiliki bidang yang diekspresikan bukan sebagai primitif, namun sebagai referensi ke objek lain? Misalnya , mari kita membuat kelas terpisah TerritoriesInfountuk digunakan bersama kelas kita . 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 + '\'' +
               '}';
   }
}
Tapi sekarang kita punya pertanyaan: haruskah semua kelas ini bisa diserialkan jika kita ingin membuat serialisasi 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 kita periksa ini dalam praktiknya! Biarkan semuanya apa adanya untuk saat ini dan coba membuat serialisasi 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();
   }
}
Hasil: Pengecualian di thread "utama" java.io.NotSerializableException: DiplomacyInfo Gagal! Sebenarnya, inilah jawaban atas pertanyaan kami. Saat Anda membuat serialisasi suatu objek, semua objek yang direferensikannya dalam variabel instannya akan diserialkan. Dan jika objek tersebut juga mereferensikan objek ketiga, objek tersebut juga diserialkan. Dan seterusnya tanpa batas. Semua kelas dalam rantai ini harus dapat diserialkan, jika tidak, kelas tersebut tidak akan dapat diserialkan dan pengecualian akan dilempar. Hal ini, tentu saja, dapat menimbulkan masalah di masa depan. Apa yang harus kita lakukan, misalnya, jika kita tidak memerlukan bagian dari kelas selama serialisasi? Atau, misalnya, TerritoryInfokita mewarisi kelas dalam program kita sebagai bagian dari suatu perpustakaan. Namun, ini tidak Dapat Diserialkan, dan oleh karena itu, kami tidak dapat mengubahnya. Ternyata kita tidak bisa menambahkan field TerritoryInfoke class kita , karena seluruh kelas akan menjadi non-serializable! Masalah:/ Masalah seperti ini diselesaikan di Java dengan menggunakan kata kunci . Jika Anda menambahkan kata kunci ini ke bidang kelas, nilai bidang ini tidak akan diserialkan. Mari kita coba membuat salah satu bidang kelas kita , setelah itu kita akan membuat serialisasi dan memulihkan satu objek. SavedGameSavedGameSerialisasi dan Deserialisasi di Java - 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='Spanyol punya 100 emas, Rusia punya 80 emas, Prancis punya 90 emas'}, diplomacyInfo=DiplomacyInfo{info='Prancis berperang dengan Rusia, Spanyol telah mengambil posisi netralitas'}} Pada saat yang sama, kami menerima jawaban atas pertanyaan tentang nilai apa yang akan diberikan transientke -field. Ini diberi nilai default. Dalam hal objek, ini adalah null. Di waktu luang Anda, Anda dapat membaca artikel bagus tentang serialisasi ini . Ini juga berbicara tentang antarmuka Externalizable, yang akan kita bicarakan pada kuliah berikutnya. Selain itu, ada bab tentang topik ini di buku “Head-First Java”, perhatikan :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION