JavaRush /Blog Java /Random-MS /Antara Muka Externalizable dalam Java

Antara Muka Externalizable dalam Java

Diterbitkan dalam kumpulan
hello! Hari ini kami akan meneruskan pengenalan kami untuk mensiri dan menyahsiri objek Java. Dalam kuliah terakhir, kami telah diperkenalkan kepada antara muka penanda Boleh Bersiri , melihat contoh penggunaannya, dan juga mempelajari cara mengawal proses bersiri menggunakan kata kunci sementara . Nah, "urus proses," sudah tentu, adalah perkataan yang kuat. Kami mempunyai satu kata kunci, satu ID versi, dan itu pada asasnya. Selebihnya proses itu "dikawatkan" di dalam Java, dan tiada akses kepadanya. Dari sudut pandangan, ini, sudah tentu, bagus. Tetapi seorang pengaturcara dalam kerjanya harus memberi tumpuan bukan sahaja pada keselesaannya sendiri, bukan? :) Terdapat faktor lain yang perlu dipertimbangkan. Oleh itu, Serializable bukan satu-satunya alat untuk pensirilan-deserialisasi di Jawa. Hari ini kita akan berkenalan dengan antara muka Externalizable . Tetapi sebelum kami meneruskan untuk mengkajinya, anda mungkin mempunyai soalan yang munasabah: mengapa kita memerlukan alat lain? SerializableSaya 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 Serializableia mempunyai beberapa kelemahan. Mari kita senaraikan beberapa daripadanya:
  1. Prestasi. Antara muka mempunyai Serializablebanyak kelebihan, tetapi prestasi tinggi jelas bukan salah satu daripada mereka.

Memperkenalkan Antara Muka Externalizable - 2

Pertama , mekanisme dalaman Serializablemenjana 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 Serializableberdasarkan 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.

  1. 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 transientuntuk mengecualikan beberapa data, dan itu sahaja. Semacam "toolkit" :/

  2. 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 Serializableia tidak memberi peluang ini.

Memperkenalkan Antara Muka Externalizable - 3Baiklah, akhirnya mari kita lihat bagaimana rupa kelas menggunakan 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, Externalizableanda 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 outdan sebagai parameter ObjectInputyang 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 Memperkenalkan Antara Muka Externalizable - 4 Oops :( Ternyata tidak begitu mudah! Mekanisme penyahserialisasian melemparkan pengecualian dan memerlukan kami mencipta pembina lalai. Saya tertanya-tanya mengapa? SerializableKami berjaya tanpanya... :/ Di sini kita datang kepada satu lagi nuansa penting . Perbezaan antara Serializabledan Externalizableterletak 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, Serializablememori 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 , Externalizablemekanisme penyahserikatan akan berbeza. Pada mulanya, pembina lalai dipanggil. Dan hanya kemudian UserInfodipanggil pada kaedah objek yang dicipta readExternal(), yang bertanggungjawab untuk mengisi medan objek. Iaitu mengapa mana-mana kelas yang melaksanakan antara muka Externalizablemesti mempunyai pembina lalai . Mari tambahkannya ke kelas kami UserInfodan 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 Serializableatau Externalizable), beri perhatian kepada pembolehubah static. Apabila digunakan, Serializablemedan ini tidak bersiri sama sekali (dan, oleh itu, nilainya tidak berubah, kerana staticmedan tersebut tergolong dalam kelas, bukan objek). Tetapi apabila menggunakannya, Externalizableanda 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, Serializableia bersiri dan dinyahsiri seperti biasa, tetapi apabila digunakan, adalah mustahil Externalizableuntuk menyahsiri finalpembolehubah ! 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 Externalizablekelas juga mesti mempunyai pembina lalai. Berikut ialah beberapa pautan ke artikel yang bagus tentang mekanisme bersiri: jumpa lagi! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION