CodeGym /Blog Java /rawak /Antara muka luaran dalam Java
John Squirrels
Tahap
San Francisco

Antara muka luaran dalam Java

Diterbitkan dalam kumpulan
Hai! Hari ini kita akan terus mengenali pensirilan dan penyahserikatan objek Java . Dalam pelajaran lepas, kami telah mengetahui antara muka penanda Boleh Bersiri , menyemak contoh penggunaannya, dan juga mempelajari cara anda boleh menggunakan kata kunci sementara untuk mengawal proses bersiri. Nah, mengatakan bahawa kita 'mengawal proses' mungkin melebih-lebihkannya. Kami mempunyai satu kata kunci, satu pengecam versi, dan itu sahaja. Selebihnya proses tersembunyi di dalam Java, dan kami tidak boleh mengaksesnya. Sudah tentu, dari segi kemudahan, ini bagus. Tetapi seorang pengaturcara tidak seharusnya hanya dipandu oleh keselesaannya sendiri, bukan? :) Ada faktor lain yang perlu anda pertimbangkan. Itulah sebabnya Serializablebukan satu-satunya mekanisme untuk pensirilan-deserialisasi di Jawa. Hari ini kita akan berkenalan dengan antara muka Externalizable . Tetapi sebelum kita mula mengkajinya, anda mungkin mempunyai soalan yang munasabah: mengapa kita memerlukan mekanisme lain? Serializablemelakukan tugasnya, dan apa yang tidak disukai tentang pelaksanaan automatik keseluruhan proses? Dan contoh yang kami lihat juga tidak rumit. Jadi apa masalahnya? Mengapa kita memerlukan antara muka lain untuk tugas yang sama? Hakikatnya ia Serializablemempunyai beberapa kekurangan. Kami menyenaraikan beberapa daripada mereka:
  1. Prestasi. Antara Serializablemuka mempunyai banyak kelebihan, tetapi prestasi tinggi jelas bukan salah satu daripada mereka.

    Memperkenalkan antara muka Externalizable - 2

    Pertama, Serializable pelaksanaan dalaman menjana sejumlah besar maklumat perkhidmatan dan semua jenis data sementara.

    Kedua, Serializable bergantung pada Reflection API (anda tidak perlu mendalami perkara ini sekarang; anda boleh membaca lebih lanjut pada masa lapang anda, jika anda berminat). Perkara ini membolehkan anda melakukan perkara yang kelihatan mustahil di Jawa: contohnya, menukar nilai medan peribadi. CodeGym mempunyai artikel yang sangat baik tentang Reflection API . Anda boleh membaca tentangnya di sana.

  2. Fleksibiliti. Kami tidak mengawal proses bersiri-deserialisasi apabila kami menggunakan Serializableantara muka.

    Di satu pihak, ia sangat mudah, kerana jika kita tidak begitu mengambil berat tentang prestasi, maka nampaknya bagus untuk tidak perlu menulis kod. Tetapi bagaimana jika kita benar-benar perlu menambah beberapa ciri kita sendiri (kami akan memberikan contoh di bawah) pada logik bersiri?

    Pada asasnya, semua yang kita perlu mengawal proses adalah transientkata kunci untuk mengecualikan beberapa data. Itu sahaja. Itulah keseluruhan kotak alat kami :/

  3. Keselamatan. Item ini diperolehi sebahagian daripada item sebelumnya.

    Kami tidak menghabiskan banyak masa memikirkan perkara ini sebelum ini, tetapi bagaimana jika sesetengah maklumat dalam kelas anda tidak ditujukan untuk mata dan telinga orang lain yang mengintip? Contoh mudah ialah kata laluan atau data pengguna peribadi yang lain, yang dalam dunia hari ini dikawal oleh sekumpulan undang-undang.

    Jika kita menggunakan Serializable, kita tidak boleh berbuat apa-apa mengenainya. Kami menyusun segala-galanya sebagaimana adanya.

    Tetapi jika kita melakukannya dengan cara yang betul, kita mesti menyulitkan data jenis ini sebelum menulisnya ke fail atau menghantarnya melalui rangkaian. Tetapi Serializabletidak memungkinkan ini.

Memperkenalkan antara muka Externalizable - 3Baiklah, akhirnya mari kita lihat bagaimana kelas akan kelihatan jika kita menggunakan antara Externalizablemuka.

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 mempunyai perubahan ketara! Yang utama adalah jelas: apabila melaksanakan Externalizableantara muka, anda mesti melaksanakan dua kaedah yang diperlukan: writeExternal()danreadExternal(). Seperti yang kami katakan sebelum ini, tanggungjawab untuk bersiri dan penyahserikatan akan terletak pada pengaturcara. Tetapi kini anda boleh menyelesaikan masalah tiada kawalan ke atas proses! Seluruh proses diprogramkan secara langsung oleh anda. Sememangnya, ini membolehkan mekanisme yang lebih fleksibel. Selain itu, masalah keselamatan diselesaikan. Seperti yang anda lihat, kelas kami mempunyai medan data peribadi yang tidak boleh disimpan tanpa disulitkan. Kini kita boleh dengan mudah menulis kod yang memenuhi kekangan ini. Sebagai contoh, kami boleh menambah dua kaedah persendirian mudah pada kelas kami untuk menyulitkan dan menyahsulit data sensitif. Kami akan menulis data pada fail dan membacanya daripada fail dalam bentuk yang disulitkan. Selebihnya data akan ditulis dan dibaca sebagaimana adanya :) Akibatnya, kelas kami 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 melaksanakan dua kaedah yang menggunakan parameter yang sama ObjectOutputdan ObjectInputyang telah kami temui dalam pelajaran tentang Serializable. Pada masa yang tepat, kami menyulitkan atau menyahsulit data yang diperlukan, dan kami menggunakan data yang disulitkan untuk mensirikan objek kami. Mari lihat bagaimana ini 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("Paul", "Piper", "Paul Piper's passport data");

       objectOutputStream.writeObject(userInfo);

       objectOutputStream.close();

   }
}
Dalam kaedah encryptString()dan decryptString(), kami secara khusus menambah output konsol untuk mengesahkan borang di mana data rahsia akan ditulis dan dibaca. Kod di atas memaparkan baris berikut: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Penyulitan berjaya! Kandungan penuh fail kelihatan seperti ini: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Sekarang mari cuba gunakan logik penyahserialisasian kami.

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, tiada apa yang kelihatan rumit di sini. Ia sepatutnya berkesan! Kami menjalankannya dan mendapatkan... Pengecualian dalam utas "utama" java.io.InvalidClassException: UserInfo; tiada pembina yang sah Memperkenalkan antara muka Externalizable - 4 Oops! :( Nampaknya, ia tidak begitu mudah! Mekanisme penyahserialisasian melemparkan pengecualian dan menuntut kami mencipta pembina lalai. Saya tertanya-tanya mengapa. Dengan Serializable, kami berjaya tanpa satu... :/ Di sini kami telah menemui satu lagi nuansa penting. perbezaan antara Serializabledan Externalizableterletak bukan sahaja pada akses 'diperluaskan' pengaturcara dan keupayaan untuk mengawal proses dengan lebih fleksibel, tetapi juga pada proses itu sendiri. Di atas semua itu, pada mekanisme penyahserikatan . Apabila menggunakanSerializable, memori hanya diperuntukkan untuk objek, dan kemudian nilai dibaca daripada strim dan digunakan untuk menetapkan medan objek. Jika kita menggunakan Serializable, pembina objek tidak dipanggil! Semua kerja berlaku melalui refleksi (API Refleksi, yang kami nyatakan secara ringkas dalam pelajaran lepas). Dengan Externalizable, mekanisme penyahserikatan adalah berbeza. Pembina lalai dipanggil dahulu. Hanya selepas itu kaedah UserInfoobjek yang dicipta readExternal()dipanggil. Ia bertanggungjawab untuk menetapkan medan objek. Itulah sebabnya mana-mana kelas yang melaksanakan Externalizableantara muka mesti mempunyai pembina lalai . Mari tambah satu pada UserInfokelas kami 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 Paul Piper UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'Data pasport Paul Piper' } Sekarang itu sesuatu yang berbeza sama sekali! Pertama, rentetan yang dinyahsulit dengan maklumat rahsia dipaparkan pada konsol. Kemudian objek yang kami pulihkan daripada fail dipaparkan sebagai rentetan! Jadi kami telah berjaya menyelesaikan semua masalah :) Topik bersiri dan penyahserikatan kelihatan mudah, tetapi, seperti yang anda lihat, pelajarannya telah lama. Dan masih banyak lagi yang belum kami bincangkan! Masih terdapat banyak kehalusan yang terlibat apabila menggunakan setiap antara muka ini. Tetapi untuk mengelak daripada meletupkan otak anda daripada maklumat baharu yang berlebihan, saya akan menyenaraikan secara ringkas beberapa perkara penting dan memberi anda pautan kepada bacaan tambahan. Jadi, apa lagi yang anda perlu tahu? Pertama , semasa penyirian (tidak kira sama ada anda menggunakan Serializableatau Externalizable), beri perhatian kepada staticpembolehubah. Apabila anda menggunakan Serializable, medan ini tidak bersiri sama sekali (dan, sewajarnya, nilainya tidak berubah, kerana staticmedan milik kelas, bukan objek). Tetapi apabila anda menggunakanExternalizable, anda mengawal proses itu sendiri, jadi secara teknikal anda boleh menyusunnya secara bersiri. Tetapi, kami tidak mengesyorkannya, kerana tindakan itu berkemungkinan menghasilkan banyak pepijat halus. Kedua , anda juga harus memberi perhatian kepada pembolehubah dengan finalpengubah suai. Apabila anda menggunakan Serializable, ia bersiri dan dinyahsiri seperti biasa, tetapi apabila anda menggunakan Externalizable, adalah mustahil untuk menyahsiri finalpembolehubah ! Sebabnya mudah: semua finalmedan dimulakan apabila pembina lalai dipanggil — selepas itu, nilainya tidak boleh diubah. Oleh itu, untuk mensiri objek yang mempunyai finalmedan, gunakan penyirian standard yang disediakan oleh Serializable. Ketiga , apabila anda menggunakan warisan, semua kelas keturunan yang mewarisi beberapaExternalizablekelas juga mesti mempunyai pembina lalai. Berikut ialah pautan kepada artikel yang bagus tentang mekanisme bersiri: Sehingga kali seterusnya! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION