CodeGym /จาวาบล็อก /สุ่ม /อินเตอร์เฟสภายนอกที่ปรับแต่งได้ใน Java
John Squirrels
ระดับ
San Francisco

อินเตอร์เฟสภายนอกที่ปรับแต่งได้ใน Java

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะมาทำความรู้จักกับซีเรียลไลเซชันและดีซีเรียลไลเซชันของออบเจกต์ Java กันต่อไป ในบทเรียนที่แล้ว เราได้รู้จัก อินเทอร์เฟซเครื่องหมาย ที่ทำให้เป็นอนุกรมได้ทบทวนตัวอย่างการใช้งาน และยังได้เรียนรู้วิธีที่คุณสามารถใช้ คำสำคัญ ชั่วคราวเพื่อควบคุมกระบวนการทำให้เป็นอนุกรมได้ การบอกว่าเรา 'ควบคุมกระบวนการ' อาจเป็นการกล่าวเกินจริง เรามีคีย์เวิร์ดหนึ่งคำ ตัวระบุเวอร์ชันเดียว แค่นั้น กระบวนการที่เหลือซ่อนอยู่ใน Java และเราไม่สามารถเข้าถึงได้ แน่นอนในแง่ของความสะดวกสบายนี่เป็นสิ่งที่ดี แต่โปรแกรมเมอร์ไม่ควรได้รับคำแนะนำจากความสะดวกสบายของตนเองเท่านั้น จริงไหม? :) มีปัจจัยอื่น ๆ ที่คุณต้องพิจารณา นั่นเป็นเหตุผลที่ทำให้เป็นอนุกรมได้ไม่ใช่กลไกเดียวสำหรับการซีเรียลไลเซชัน-ดีซีเรียลไลเซชันใน Java วันนี้เราจะมาทำความรู้จักกับอินเทอร์เฟซที่ปรับแต่งภายนอกได้ แต่ก่อนที่เราจะเริ่มศึกษา คุณอาจมีคำถามที่สมเหตุสมผลว่าทำไมเราถึงต้องการกลไกอื่น Serializableทำหน้าที่ของมัน และอะไรที่ไม่ชอบเกี่ยวกับการนำกระบวนการทั้งหมดไปใช้โดยอัตโนมัติ และตัวอย่างที่เราดูก็ไม่ซับซ้อนเช่นกัน แล้วปัญหาคืออะไร? เหตุใดเราจึงต้องการอินเทอร์เฟซอื่นสำหรับงานเดียวกัน ความจริงก็คือSerializableมีข้อบกพร่องหลายประการ เราแสดงรายการบางส่วน:
  1. ผลงาน. อินSerializableเทอร์เฟซมีข้อดีมากมาย แต่ประสิทธิภาพสูงนั้นไม่ใช่หนึ่งในนั้นอย่างชัดเจน

    ขอแนะนำอินเทอร์เฟซที่ปรับแต่งภายนอกได้ - 2

    ขั้นแรก Serializableการดำเนินการภายในของ ' จะสร้างข้อมูลบริการจำนวนมากและข้อมูลชั่วคราวทุกประเภท

    ประการที่สอง Serializableใช้ Reflection API (ตอนนี้คุณไม่ต้องลงลึกในเรื่องนี้แล้ว คุณสามารถอ่านเพิ่มเติมได้ในยามว่าง หากคุณสนใจ) สิ่งนี้ช่วยให้คุณทำสิ่งที่ดูเหมือนจะเป็นไปไม่ได้ใน Java เช่น เปลี่ยนค่าของฟิลด์ส่วนตัว CodeGym มีบทความที่ยอดเยี่ยมเกี่ยวกับ Reflection API คุณสามารถอ่านเกี่ยวกับเรื่องนี้ได้ที่นั่น

  2. ความยืดหยุ่น เราไม่ได้ควบคุมกระบวนการซีเรียลไลเซชัน-ดีซีเรียลไลเซชันเมื่อเราใช้Serializableอินเทอร์เฟซ

    ในแง่หนึ่ง มันสะดวกมาก เพราะหากเราไม่กังวลเรื่องประสิทธิภาพเป็นพิเศษ ก็ดูเหมือนจะดีที่ไม่ต้องเขียนโค้ด แต่จะเป็นอย่างไรหากเราต้องการเพิ่มคุณสมบัติบางอย่างของเราเอง (เราจะให้ตัวอย่างด้านล่าง) ให้กับตรรกะการทำให้เป็นอนุกรม

    โดยพื้นฐานแล้ว สิ่งที่เราต้องควบคุมกระบวนการคือtransientคีย์เวิร์ดเพื่อยกเว้นข้อมูลบางอย่าง แค่นั้นแหละ. นั่นคือกล่องเครื่องมือทั้งหมดของเรา :/

  3. ความปลอดภัย. รายการนี้มีส่วนมาจากรายการที่แล้ว

    เราไม่ได้ใช้เวลามากในการคิดเรื่องนี้มาก่อน แต่จะเป็นอย่างไรหากข้อมูลบางอย่างในชั้นเรียนของคุณไม่ได้มีไว้สำหรับสอดรู้สอดเห็น ตัวอย่างง่ายๆ เช่น รหัสผ่านหรือข้อมูลส่วนตัวอื่นๆ ของผู้ใช้ ซึ่งในโลกปัจจุบันอยู่ภายใต้กฎหมายหลายฉบับ

    ถ้าเราใช้ เราSerializableไม่สามารถทำอะไรกับมันได้เลย เราทำให้ทุกอย่างเป็นอนุกรมตามที่เป็นอยู่

    แต่ถ้าเราทำอย่างถูกวิธี เราจะต้องเข้ารหัสข้อมูลประเภทนี้ก่อนที่จะเขียนลงไฟล์หรือส่งผ่านเครือข่าย แต่Serializableไม่ทำให้สิ่งนี้เป็นไปได้

ขอแนะนำอินเทอร์เฟซที่ปรับแต่งภายนอกได้ - 3เรามาดูกันว่าคลาสจะมีลักษณะอย่างไรหากเราใช้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 {

   }
}
อย่างที่คุณเห็น เรามีการเปลี่ยนแปลงที่สำคัญ! สิ่งหลักนั้นชัดเจน: เมื่อใช้Externalizableอินเทอร์เฟซคุณต้องใช้วิธีที่จำเป็นสองวิธี: writeExternal()และreadExternal(). อย่างที่เราได้กล่าวไปก่อนหน้านี้ ความรับผิดชอบในการซีเรียลไลเซชันและดีซีเรียลไลเซชันจะอยู่กับโปรแกรมเมอร์ แต่ตอนนี้คุณสามารถแก้ปัญหาที่ไม่สามารถควบคุมกระบวนการได้แล้ว! กระบวนการทั้งหมดได้รับการตั้งโปรแกรมโดยตรงจากคุณ โดยธรรมชาติแล้วสิ่งนี้ทำให้กลไกมีความยืดหยุ่นมากขึ้น นอกจากนี้ยังแก้ปัญหาเกี่ยวกับความปลอดภัย อย่างที่คุณเห็น ชั้นเรียนของเรามีช่องข้อมูลส่วนบุคคลที่ไม่สามารถจัดเก็บโดยไม่เข้ารหัสได้ ตอนนี้เราสามารถเขียนโค้ดที่ตอบสนองข้อจำกัดนี้ได้อย่างง่ายดาย ตัวอย่างเช่น เราสามารถเพิ่มวิธีการส่วนตัวอย่างง่ายสองวิธีในชั้นเรียนของเราเพื่อเข้ารหัสและถอดรหัสข้อมูลที่ละเอียดอ่อน เราจะเขียนข้อมูลลงในไฟล์และอ่านจากไฟล์ในรูปแบบการเข้ารหัส ข้อมูลที่เหลือจะถูกเขียนและอ่านเหมือนเดิม :) ด้วยเหตุนี้ คลาสของเราจึงมีลักษณะดังนี้:

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;
   }
}
เราใช้สองวิธีที่ใช้เหมือนกันObjectOutputและObjectInputพารามิเตอร์ที่เราพบแล้วในบทเรียนเกี่ยวSerializableกับ ในเวลาที่เหมาะสม เราเข้ารหัสหรือถอดรหัสข้อมูลที่จำเป็น และเราใช้ข้อมูลที่เข้ารหัสเพื่อทำให้วัตถุของเราเป็นอนุกรม มาดูกันว่าสิ่งนี้มีลักษณะอย่างไรในทางปฏิบัติ:

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();

   }
}
ในencryptString()และdecryptString()วิธีการ เราได้เพิ่มเอาต์พุตคอนโซลโดยเฉพาะเพื่อตรวจสอบรูปแบบที่จะเขียนและอ่านข้อมูลลับ รหัสด้านบนแสดงบรรทัดต่อไปนี้: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh การเข้ารหัสสำเร็จ! เนื้อหาทั้งหมดของไฟล์มีลักษณะดังนี้: ¬н sr UserInfoГ!}ҐџC'ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx ทีนี้มาลองใช้ตรรกะการแยกซีเรียลไลเซชันกัน

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();

   }
}
ดูเหมือนไม่มีอะไรซับซ้อนที่นี่ มันควรจะทำงาน! เราเรียกใช้และได้รับ... ข้อยกเว้นในเธรด "หลัก" java.io.InvalidClassException: UserInfo; ไม่มีตัวสร้างที่ถูกต้อง แนะนำอินเทอร์เฟซที่ปรับแต่งภายนอกได้ - 4อ๊ะ! :( เห็นได้ชัดว่ามันไม่ง่ายเลย! กลไกการดีซีเรียลไลเซชันมีข้อยกเว้นและเรียกร้องให้เราสร้างตัวสร้างเริ่มต้น ฉันสงสัยว่าทำไม ด้วย , Serializableเราได้มาโดยปราศจากหนึ่ง... :/ ที่นี่เราพบความแตกต่างที่สำคัญอีกประการหนึ่ง ความแตกต่างระหว่างSerializableและExternalizableไม่ได้อยู่ที่การเข้าถึง 'ขยาย' ของโปรแกรมเมอร์และความสามารถในการควบคุมกระบวนการที่ยืดหยุ่นมากขึ้นเท่านั้น แต่ยังรวมถึง กระบวนการด้วย เหนือสิ่งอื่นใดในกลไกการดีซีเรียลไลเซชัน เมื่อใช้Serializableหน่วยความจำจะถูกจัดสรรให้กับออบเจกต์ จากนั้นค่าจะถูกอ่านจากสตรีมและใช้เพื่อตั้งค่าฟิลด์ของออบเจ็กต์ ถ้าเราใช้Serializableตัวสร้างของวัตถุจะไม่ถูกเรียก! งานทั้งหมดเกิดขึ้นผ่านการไตร่ตรอง (Reflection API ซึ่งเราได้กล่าวถึงสั้นๆ ในบทเรียนที่แล้ว) ด้วยExternalizableกลไกการดีซีเรียลไลเซชันจะแตกต่างกัน ตัวสร้างเริ่มต้นถูกเรียกก่อน หลังจากนั้นจะเรียกวิธีการUserInfoของวัตถุ ที่สร้างขึ้น readExternal()มีหน้าที่ในการตั้งค่าฟิลด์ของวัตถุ นั่นคือเหตุผลที่คลาสใด ๆ ที่ใช้Externalizableอินเทอร์เฟซต้องมีตัวสร้างเริ่มต้น มาเพิ่มในUserInfoชั้นเรียนของเราและรันโค้ดอีกครั้ง:

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();
   }
}
เอาต์พุตของคอนโซล: ข้อมูลหนังสือเดินทางของ Paul Piper UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'ข้อมูลหนังสือเดินทางของ Paul Piper' } ตอนนี้เป็นสิ่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิง! ขั้นแรก สตริงที่ถอดรหัสพร้อมข้อมูลลับจะแสดงบนคอนโซล จากนั้นวัตถุที่เรากู้คืนจากไฟล์จะแสดงเป็นสตริง! ดังนั้นเราจึงแก้ไขปัญหาทั้งหมดได้สำเร็จ :) หัวข้อของการทำให้เป็นอนุกรมและการดีซีเรียลไลเซชันดูเหมือนง่าย แต่อย่างที่คุณเห็น บทเรียนมีมานานแล้ว และยังมีอีกมากมายที่เรายังไม่ได้กล่าวถึง! ยังมีรายละเอียดปลีกย่อยมากมายที่เกี่ยวข้องเมื่อใช้แต่ละอินเทอร์เฟซเหล่านี้ แต่เพื่อหลีกเลี่ยงการระเบิดสมองของคุณจากข้อมูลใหม่ที่มากเกินไป ฉันจะทำรายการประเด็นสำคัญสั้นๆ สองสามข้อและให้ลิงก์ไปยังการอ่านเพิ่มเติมแก่คุณ แล้วคุณต้องรู้อะไรอีกบ้าง? ขั้นแรกในระหว่างการทำให้เป็นอนุกรม (ไม่ว่าคุณจะใช้Serializableหรือ ก็ตามExternalizable) ให้ใส่ใจกับ staticตัวแปร เมื่อคุณใช้Serializableฟิลด์เหล่านี้จะไม่ถูกทำให้เป็นอนุกรมเลย (และดังนั้น ค่าของฟิลด์จะไม่เปลี่ยนแปลง เนื่องจากstaticฟิลด์เป็นของคลาส ไม่ใช่วัตถุ) แต่เมื่อคุณใช้Externalizableคุณเป็นผู้ควบคุมกระบวนการด้วยตนเอง ดังนั้นในทางเทคนิคแล้ว คุณสามารถทำให้เป็นอนุกรมได้ แต่เราไม่แนะนำ เนื่องจากการกระทำดังกล่าวมีแนวโน้มที่จะสร้างข้อบกพร่องเล็กน้อยมากมาย ประการที่สองคุณควรใส่ใจกับตัวแปรที่มีการfinalปรับเปลี่ยน ด้วย เมื่อคุณใช้Serializableตัวแปรเหล่านี้จะถูกทำให้เป็นซีเรียลไลซ์และดีซีเรียลไลซ์ตามปกติ แต่เมื่อคุณใช้Externalizableจะไม่สามารถทำการดีซีเรียลไลซ์finalตัวแปร ได้ ! เหตุผลนั้นง่ายมาก: finalฟิลด์ทั้งหมดจะถูกเตรียมใช้งานเมื่อมีการเรียกใช้ตัวสร้างเริ่มต้น — หลังจากนั้นจะไม่สามารถเปลี่ยนแปลงค่าได้ ดังนั้น ในการทำให้วัตถุมีฟิลด์เป็นอนุกรมfinalให้ใช้การทำให้เป็นอันดับมาตรฐานที่มีให้Serializableโดย ประการที่สามเมื่อคุณใช้การสืบทอด คลาสลูกหลานทั้งหมดที่สืบทอดมาบางส่วนExternalizableคลาสต้องมีตัวสร้างเริ่มต้นด้วย นี่คือลิงค์ไปยังบทความที่ดีเกี่ยวกับกลไกการออกซีเรียล: จนกว่าจะถึงครั้งต่อไป! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION