Serializable
Saya mengatasi tugas saya, dan pelaksanaan automatik keseluruhan proses tidak boleh tidak bergembira. Contoh yang kami lihat juga tidak rumit. Jadi apa urusannya? Mengapa antara muka lain untuk tugas yang sama? Hakikatnya Serializable
ia mempunyai beberapa kelemahan. Mari kita senaraikan beberapa daripadanya:
-
Prestasi. Antara muka mempunyai
Serializable
banyak kelebihan, tetapi prestasi tinggi jelas bukan salah satu daripada mereka.
Pertama , mekanisme dalaman Serializable
menjana sejumlah besar maklumat perkhidmatan dan pelbagai jenis data sementara semasa operasi.
Kedua (anda tidak perlu pergi ke ini sekarang dan membaca pada masa lapang anda jika anda berminat), kerja itu Serializable
berdasarkan penggunaan API Refleksi. Alat ini membolehkan anda melakukan perkara yang kelihatan mustahil di Jawa: contohnya, menukar nilai medan peribadi. JavaRush mempunyai artikel yang sangat baik tentang Reflection API , anda boleh membaca tentangnya di sini.
-
Fleksibiliti. Kami tidak mengawal proses pensirisian-penyahserikatan sama sekali apabila menggunakan
Serializable
.Di satu pihak, ini sangat mudah, kerana jika kita tidak begitu mementingkan prestasi, keupayaan untuk tidak menulis kod kelihatan mudah. Tetapi bagaimana jika kita benar-benar perlu menambah beberapa ciri kita sendiri (contoh salah satu daripadanya akan berada di bawah) pada logik bersiri?
Pada asasnya, semua yang kita perlu mengawal proses ialah kata kunci
transient
untuk mengecualikan beberapa data, dan itu sahaja. Semacam "toolkit" :/ -
Keselamatan. Perkara ini sebahagiannya mengikuti daripada yang sebelumnya.
Kami tidak terlalu memikirkan perkara ini sebelum ini, tetapi bagaimana jika sesetengah maklumat dalam kelas anda tidak ditujukan untuk "telinga orang lain" (lebih tepat lagi, mata)? Contoh mudah ialah kata laluan atau data peribadi pengguna yang lain, yang dalam dunia moden dikawal oleh sekumpulan undang-undang.
Menggunakan
Serializable
, kami sebenarnya tidak boleh berbuat apa-apa mengenainya. Kami menyusun segala-galanya sebagaimana adanya.Tetapi, dengan cara yang baik, kita mesti menyulitkan data jenis ini sebelum menulisnya ke fail atau menghantarnya melalui rangkaian. Tetapi
Serializable
ia tidak memberi peluang ini.
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;
//...конструктор, геттеры, сеттеры, toString()...
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
Seperti yang anda lihat, kami telah membuat perubahan yang ketara! Yang utama adalah jelas: apabila melaksanakan antara muka, Externalizable
anda mesti melaksanakan dua kaedah mandatori - writeExternal()
dan readExternal()
. Seperti yang kami katakan sebelum ini, semua tanggungjawab untuk bersiri dan penyahserikatan akan terletak pada pengaturcara. Walau bagaimanapun, kini anda boleh menyelesaikan masalah kekurangan kawalan ke atas proses ini! Keseluruhan proses diprogramkan secara langsung oleh anda, yang, tentu saja, mewujudkan mekanisme yang lebih fleksibel. Selain itu, masalah keselamatan juga dapat diselesaikan. Seperti yang anda lihat, kami mempunyai medan dalam kelas kami: data peribadi yang tidak boleh disimpan tanpa disulitkan. Kini kita boleh dengan mudah menulis kod yang memenuhi kekangan ini. Sebagai contoh, tambahkan dua kaedah persendirian mudah pada kelas kami untuk menyulitkan dan menyahsulit data rahsia. Kami akan menulisnya ke fail dan membacanya daripada fail dalam bentuk yang disulitkan. Dan kami akan menulis dan membaca data yang selebihnya sebagaimana adanya :) Akibatnya, kelas kami akan kelihatan 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 telah melaksanakan dua kaedah yang menggunakan parameter yang sama ObjectOutput out
dan sebagai parameter ObjectInput
yang telah kami temui dalam kuliah tentang Serializable
. Pada masa yang tepat, kami menyulitkan atau menyahsulit data yang diperlukan, dan dalam bentuk ini kami menggunakannya untuk mensirikan objek kami. Mari lihat bagaimana ini akan kelihatan dalam amalan:
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("Ivan", "Ivanov", "Ivan Ivanov's passport data");
objectOutputStream.writeObject(userInfo);
objectOutputStream.close();
}
}
Dalam kaedah encryptString()
dan decryptString()
, kami secara khusus menambah output pada konsol untuk menyemak dalam bentuk apa data rahsia akan ditulis dan dibaca. Kod di atas mengeluarkan baris berikut ke konsol: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Penyulitan berjaya! Kandungan penuh fail kelihatan seperti ini: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Sekarang mari cuba gunakan logik penyahserialisasian yang kami tulis.
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();
}
}
Nah, nampaknya tidak ada apa-apa yang rumit di sini, ia sepatutnya berfungsi! Mari jalankan... Exception dalam thread "main" java.io.InvalidClassException: UserInfo; tiada pembina yang sah Oops :( Ternyata tidak begitu mudah! Mekanisme penyahserialisasian melemparkan pengecualian dan memerlukan kami mencipta pembina lalai. Saya tertanya-tanya mengapa? Serializable
Kami berjaya tanpanya... :/ Di sini kita datang kepada satu lagi nuansa penting . Perbezaan antara Serializable
dan Externalizable
terletak bukan sahaja pada akses "dilanjutkan" untuk pengaturcara dan keupayaan untuk mengurus proses dengan lebih fleksibel, tetapi juga pada proses itu sendiri. Pertama sekali, dalam mekanisme penyahserikatan ... Apabila digunakan, Serializable
memori hanyalah diperuntukkan untuk objek, selepas itu nilai dibaca dari aliran, yang mengisi semua medannya. Jika kita menggunakan Serializable
, pembina objek tidak dipanggil! Semua kerja dilakukan melalui refleksi (API Refleksi, yang kami nyatakan secara ringkas pada yang terakhir kuliah). Dalam kes , Externalizable
mekanisme penyahserikatan akan berbeza. Pada mulanya, pembina lalai dipanggil. Dan hanya kemudian UserInfo
dipanggil pada kaedah objek yang dicipta readExternal()
, yang bertanggungjawab untuk mengisi medan objek. Iaitu mengapa mana-mana kelas yang melaksanakan antara muka Externalizable
mesti mempunyai pembina lalai . Mari tambahkannya ke kelas kami UserInfo
dan jalankan semula kod:
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();
}
}
Output konsol: Data pasport Ivan Ivanov UserInfo{firstName='Ivan', lastName='Ivanov', superSecretInformation='Data pasport Ivan Ivanov'} Perkara yang sama sekali berbeza! Mula-mula, rentetan yang dinyahsulit dengan data rahsia telah dikeluarkan ke konsol, dan kemudian objek kami pulih daripada fail dalam format rentetan! Beginilah cara kami berjaya menyelesaikan semua masalah :) Topik bersiri dan penyahserikatan nampaknya mudah, tetapi seperti yang anda lihat, kuliah kami ternyata panjang. Dan bukan itu sahaja! Terdapat banyak lagi kehalusan apabila menggunakan setiap antara muka ini, tetapi supaya sekarang otak anda tidak meletup daripada jumlah maklumat baru, saya akan menyenaraikan secara ringkas beberapa perkara yang lebih penting dan menyediakan pautan kepada bacaan tambahan. Jadi apa lagi yang anda perlu tahu? Mula-mula , apabila bersiri (tidak kira sama ada anda menggunakan Serializable
atau Externalizable
), beri perhatian kepada pembolehubah static
. Apabila digunakan, Serializable
medan ini tidak bersiri sama sekali (dan, oleh itu, nilainya tidak berubah, kerana static
medan tersebut tergolong dalam kelas, bukan objek). Tetapi apabila menggunakannya, Externalizable
anda mengawal proses itu sendiri, jadi secara teknikal ini boleh dilakukan. Tetapi ia tidak disyorkan, kerana ini penuh dengan ralat halus. Kedua , perhatian juga harus diberikan kepada pembolehubah dengan pengubah suai final
. Apabila digunakan, Serializable
ia bersiri dan dinyahsiri seperti biasa, tetapi apabila digunakan, adalah mustahil Externalizable
untuk menyahsiri final
pembolehubah ! Sebabnya mudah: semua final
-medan dimulakan apabila pembina lalai dipanggil, dan selepas itu nilainya tidak boleh diubah. Oleh itu, untuk mensiri objek yang mengandungi final
-fields, gunakan penyirian standard melalui Serializable
. Ketiga , apabila menggunakan warisan, semua kelas pewarisan yang turun dari beberapa Externalizable
kelas juga mesti mempunyai pembina lalai. Berikut ialah beberapa pautan ke artikel yang bagus tentang mekanisme bersiri:
jumpa lagi! :)
GO TO FULL VERSION