Serializable
melakukan tugasnya, dan apa yang tidak disukai tentang penerapan otomatis seluruh proses? Dan contoh yang kami lihat juga tidak rumit. Jadi apa masalahnya? Mengapa kita membutuhkan antarmuka lain untuk tugas yang pada dasarnya sama? Faktanya adalah bahwa Serializable
memiliki beberapa kekurangan. Kami mencantumkan beberapa di antaranya:
-
Pertunjukan. Antarmuka
Serializable
memiliki banyak keunggulan, tetapi kinerja tinggi jelas bukan salah satunya.Pertama,
Serializable
implementasi internal menghasilkan sejumlah besar informasi layanan dan semua jenis data sementara.Kedua,
Serializable
bergantung pada API Refleksi (Anda tidak perlu mendalami ini sekarang; Anda dapat membaca lebih lanjut di waktu luang, jika Anda tertarik). Hal ini memungkinkan Anda melakukan hal-hal yang tampaknya mustahil di Jawa: misalnya, mengubah nilai bidang privat. CodeGym memiliki artikel bagus tentang Reflection API . Anda dapat membacanya di sana. -
Fleksibilitas. Kami tidak mengontrol proses serialisasi-deserialisasi saat kami menggunakan antarmuka
Serializable
.Di satu sisi, ini sangat nyaman, karena jika kita tidak terlalu peduli dengan kinerja, sepertinya bagus untuk tidak menulis kode. Tetapi bagaimana jika kita benar-benar perlu menambahkan beberapa fitur kita sendiri (kami akan memberikan contohnya di bawah) ke logika serialisasi?
Pada dasarnya, yang kita miliki untuk mengontrol proses adalah
transient
kata kunci untuk mengecualikan beberapa data. Itu dia. Itulah seluruh kotak peralatan kami :/ -
Keamanan. Item ini sebagian berasal dari item sebelumnya.
Kami belum menghabiskan banyak waktu untuk memikirkan hal ini sebelumnya, tetapi bagaimana jika beberapa informasi di kelas Anda tidak ditujukan untuk mata dan telinga orang lain yang ingin tahu? Contoh sederhananya adalah kata sandi atau data pengguna pribadi lainnya, yang di dunia sekarang ini diatur oleh banyak undang-undang.
Jika kita menggunakan
Serializable
, kita tidak bisa berbuat apa-apa. Kami membuat cerita bersambung semuanya apa adanya.Namun jika kita melakukannya dengan cara yang benar, kita harus mengenkripsi jenis data ini sebelum menulisnya ke file atau mengirimkannya melalui jaringan. Tapi
Serializable
tidak membuat ini mungkin.

Externalizable
.
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class UserInfo implements Externalizable {
private String firstName;
private String lastName;
private String superSecretInformation;
private static final long SERIAL_VERSION_UID = 1L;
// ...constructor, getters, setters, toString()...
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
Seperti yang Anda lihat, kami memiliki perubahan signifikan! Yang utama jelas: saat mengimplementasikan antarmuka Externalizable
, Anda harus mengimplementasikan dua metode yang diperlukan: writeExternal()
danreadExternal()
. Seperti yang kami katakan sebelumnya, tanggung jawab untuk serialisasi dan deserialisasi akan berada di tangan pemrogram. Tapi sekarang Anda bisa memecahkan masalah tanpa kendali atas prosesnya! Seluruh proses diprogram langsung oleh Anda. Secara alami, ini memungkinkan mekanisme yang jauh lebih fleksibel. Selain itu, masalah dengan keamanan terpecahkan. Seperti yang Anda lihat, kelas kami memiliki bidang data pribadi yang tidak dapat disimpan tanpa enkripsi. Sekarang kita dapat dengan mudah menulis kode yang memenuhi batasan ini. Misalnya, kita dapat menambahkan ke kelas kita dua metode privat sederhana untuk mengenkripsi dan mendekripsi data sensitif. Kami akan menulis data ke file dan membacanya dari file dalam bentuk terenkripsi. Data lainnya akan ditulis dan dibaca apa adanya :) Hasilnya, kelas kita terlihat seperti ini:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Base64;
public class UserInfo implements Externalizable {
private String firstName;
private String lastName;
private String superSecretInformation;
private static final long serialVersionUID = 1L;
public UserInfo() {
}
public UserInfo(String firstName, String lastName, String superSecretInformation) {
this.firstName = firstName;
this.lastName = lastName;
this.superSecretInformation = superSecretInformation;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.getFirstName());
out.writeObject(this.getLastName());
out.writeObject(this.encryptString(this.getSuperSecretInformation()));
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
firstName = (String) in.readObject();
lastName = (String) in.readObject();
superSecretInformation = this.decryptString((String) in.readObject());
}
private String encryptString(String data) {
String encryptedData = Base64.getEncoder().encodeToString(data.getBytes());
System.out.println(encryptedData);
return encryptedData;
}
private String decryptString(String data) {
String decrypted = new String(Base64.getDecoder().decode(data));
System.out.println(decrypted);
return decrypted;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getSuperSecretInformation() {
return superSecretInformation;
}
}
Kami menerapkan dua metode yang menggunakan parameter yang sama ObjectOutput
dan ObjectInput
yang sudah kami temui di pelajaran tentang Serializable
. Pada saat yang tepat, kami mengenkripsi atau mendekripsi data yang diperlukan, dan kami menggunakan data terenkripsi untuk membuat serial objek kami. Mari kita lihat bagaimana tampilannya dalam praktik:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
UserInfo userInfo = new UserInfo("Paul", "Piper", "Paul Piper's passport data");
objectOutputStream.writeObject(userInfo);
objectOutputStream.close();
}
}
Dalam metode encryptString()
and decryptString()
, kami secara khusus menambahkan keluaran konsol untuk memverifikasi formulir di mana data rahasia akan ditulis dan dibaca. Kode di atas menampilkan baris berikut: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Enkripsi berhasil! Isi lengkap file terlihat seperti ini: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Sekarang mari kita coba menggunakan logika deserialisasi kita.
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);
UserInfo userInfo = (UserInfo) objectInputStream.readObject();
System.out.println(userInfo);
objectInputStream.close();
}
}
Yah, sepertinya tidak ada yang rumit di sini. Ini harus bekerja! Kami menjalankannya dan mendapatkan... Pengecualian di thread "main" java.io.InvalidClassException: UserInfo; tidak ada konstruktor yang valid 
Serializable
, kami berhasil tanpa satu... :/ Di sini kami menemukan nuansa penting lainnya. The perbedaan antara Serializable
dan Externalizable
tidak hanya terletak pada akses 'perluasan' pemrogram dan kemampuan untuk mengontrol proses secara lebih fleksibel, tetapi juga pada proses itu sendiri. Di atas segalanya, dalam mekanisme deserialisasi .Serializable
, memori hanya dialokasikan untuk objek, lalu nilai dibaca dari aliran dan digunakan untuk menyetel bidang objek. Jika kita menggunakan Serializable
, konstruktor objek tidak dipanggil! Semua pekerjaan terjadi melalui refleksi (Refleksi API, yang kami sebutkan secara singkat di pelajaran terakhir). Dengan Externalizable
, mekanisme deserialization berbeda. Konstruktor default dipanggil terlebih dahulu. Hanya setelah itu metode UserInfo
objek yang dibuat readExternal()
dipanggil. Ini bertanggung jawab untuk mengatur bidang objek. Itu sebabnya setiap kelas yang mengimplementasikan Externalizable
antarmuka harus memiliki konstruktor default . Mari tambahkan satu ke UserInfo
kelas kita dan jalankan kembali kodenya:
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);
UserInfo userInfo = (UserInfo) objectInputStream.readObject();
System.out.println(userInfo);
objectInputStream.close();
}
}
Keluaran konsol: data paspor Paul Piper UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'data paspor Paul Piper' } Nah, itu sesuatu yang sama sekali berbeda! Pertama, string yang didekripsi dengan informasi rahasia ditampilkan di konsol. Kemudian objek yang kami pulihkan dari file ditampilkan sebagai string! Jadi kami berhasil menyelesaikan semua masalah :) Topik serialisasi dan deserialisasi tampaknya sederhana, tetapi, seperti yang Anda lihat, pelajarannya panjang. Dan masih banyak lagi yang belum kami bahas! Masih banyak seluk-beluk yang terlibat saat menggunakan masing-masing antarmuka ini. Tetapi untuk menghindari ledakan otak Anda dari informasi baru yang berlebihan, saya akan membuat daftar singkat beberapa poin penting dan memberi Anda tautan ke bacaan tambahan. Jadi, apa lagi yang perlu Anda ketahui? Pertama , selama serialisasi (terlepas dari apakah Anda menggunakan Serializable
atau Externalizable
), perhatikan static
variabel. Saat Anda menggunakan Serializable
, bidang ini tidak diserialisasi sama sekali (dan, karenanya, nilainya tidak berubah, karena static
bidang adalah milik kelas, bukan objek). Tetapi ketika Anda menggunakanExternalizable
, Anda mengontrol sendiri prosesnya, jadi secara teknis Anda dapat membuat cerita bersambung. Namun, kami tidak merekomendasikannya, karena tindakan tersebut kemungkinan besar akan menimbulkan banyak bug yang tidak kentara. Kedua , Anda juga harus memperhatikan variabel dengan final
pengubah. Saat Anda menggunakan Serializable
, mereka diserialkan dan dideserialisasi seperti biasa, tetapi saat Anda menggunakan Externalizable
, tidak mungkin untuk melakukan deserialisasi final
variabel ! Alasannya sederhana: semua final
bidang diinisialisasi saat konstruktor default dipanggil — setelah itu, nilainya tidak dapat diubah. Oleh karena itu, untuk membuat serial objek yang memiliki final
kolom, gunakan serialisasi standar yang disediakan oleh Serializable
. Ketiga , ketika Anda menggunakan pewarisan, semua kelas turunan mewarisi beberapaExternalizable
kelas juga harus memiliki konstruktor default. Berikut ini tautan ke artikel bagus tentang mekanisme serialisasi:
Sampai Lain waktu! :)
Lebih banyak membaca: |
---|
GO TO FULL VERSION