JavaRush /จาวาบล็อก /Random-TH /อินเทอร์เฟซภายนอกใน Java

อินเทอร์เฟซภายนอกใน Java

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

การแนะนำอินเทอร์เฟซภายนอก - 2

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

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

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

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

  2. ความปลอดภัย. จุดนี้บางส่วนต่อจากจุดก่อนหน้า

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

    การใช้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;

   //...конструктор, геттеры, сеттеры, 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 outและ เป็นพารามิเตอร์ 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("Ivan", "Ivanov", "Ivan Ivanov's passport data");

       objectOutputStream.writeObject(userInfo);

       objectOutputStream.close();

   }
}
ในencryptString()และ วิธีการ decryptString()เราได้เพิ่มเอาต์พุตลงในคอนโซลโดยเฉพาะเพื่อตรวจสอบว่าข้อมูลลับจะถูกเขียนและอ่านในรูปแบบใด รหัสด้านบนส่งออกบรรทัดต่อไปนี้ไปยังคอนโซล: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Encryption สำเร็จ! เนื้อหาทั้งหมดของไฟล์มีลักษณะดังนี้: ¢н 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();

   }
}
ดูเหมือนจะไม่มีอะไรซับซ้อนที่นี่ มันควรจะได้ผล! มาเริ่มกันเลย... ข้อยกเว้นในเธรด "main" java.io.InvalidClassException: UserInfo; ไม่มีตัวสร้างที่ถูกต้อง การแนะนำอินเทอร์เฟซภายนอก - 4อ๊ะ :( มันกลายเป็นว่าไม่ง่ายนัก! กลไกการดีซีเรียลไลเซชันมีข้อยกเว้นและกำหนดให้เราต้องสร้างตัวสร้างเริ่มต้น ฉันสงสัยว่าทำไมSerializableเราจัดการโดยไม่มีมัน... :/ เรามาถึงความแตกต่างที่สำคัญอีกอย่างหนึ่งแล้ว . ความแตกต่างระหว่างSerializableและExternalizableไม่เพียงแต่อยู่ในการเข้าถึงแบบ "ขยาย" สำหรับโปรแกรมเมอร์และความสามารถในการจัดการกระบวนการได้อย่างยืดหยุ่นมากขึ้น แต่ยังอยู่ในกระบวนการด้วย ประการแรก ในกลไกการดีซีเรียลไลซ์เซชัน ... เมื่อใช้Serializableหน่วยความจำเป็นเพียง จัดสรรให้กับวัตถุหลังจากนั้นจะอ่านค่าจากสตรีมซึ่งกรอกข้อมูลในฟิลด์ทั้งหมด หากเราใช้Serializableตัวสร้างวัตถุจะไม่ถูกเรียก!งานทั้งหมดเสร็จสิ้นผ่านการสะท้อนกลับ (Reflection API ซึ่งเรากล่าวถึงสั้น ๆ ในตอนสุดท้าย การบรรยาย) ในกรณีของExternalizableกลไกการดีซีเรียลไลเซชันจะแตกต่างออกไป ในตอนเริ่มต้น Constructor เริ่มต้นจะถูกเรียก และหลังจากนั้นเท่านั้น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();
   }
}
เอาต์พุตคอนโซล: ข้อมูลหนังสือเดินทางของ Ivan Ivanov UserInfo{firstName='Ivan', LastName='Ivanov', superSecretInformation='Ivan Ivanov's Passport data'} แตกต่างไปจากเดิมอย่างสิ้นเชิง! ขั้นแรก สตริงที่ถอดรหัสพร้อมข้อมูลลับจะถูกส่งออกไปยังคอนโซล จากนั้นอ็อบเจ็กต์ของเราจะถูกกู้คืนจากไฟล์ในรูปแบบสตริง! นี่คือวิธีที่เราแก้ไขปัญหาทั้งหมดได้สำเร็จ :) หัวข้อเรื่องซีเรียลไลซ์เซชันและดีซีเรียลไลเซชันดูเหมือนจะง่าย แต่อย่างที่คุณเห็น การบรรยายของเราใช้เวลานาน และนั่นไม่ใช่ทั้งหมด! มีรายละเอียดปลีกย่อยอีกมากมายเมื่อใช้แต่ละอินเทอร์เฟซเหล่านี้ แต่ตอนนี้สมองของคุณไม่ระเบิดจากข้อมูลใหม่จำนวนมาก ฉันจะแสดงรายการประเด็นสำคัญอีกสองสามข้อโดยย่อและให้ลิงก์ไปยังการอ่านเพิ่มเติม แล้วคุณต้องรู้อะไรอีกบ้าง? ประการแรกเมื่อทำให้เป็นอนุกรม (ไม่สำคัญว่าคุณจะใช้Serializableหรือ ) ให้Externalizableใส่ใจกับตัวแปร staticเมื่อใช้Serializableฟิลด์เหล่านี้จะไม่ถูกทำให้เป็นอนุกรมเลย (และด้วยเหตุนี้ ค่าของฟิลด์เหล่านี้จึงไม่เปลี่ยนแปลง เนื่องจากstaticฟิลด์เป็นของคลาส ไม่ใช่อ็อบเจ็กต์) แต่เมื่อใช้มันExternalizableคุณจะควบคุมกระบวนการด้วยตัวเอง ดังนั้นในทางเทคนิคจึงสามารถทำได้ แต่ไม่แนะนำ เนื่องจากเต็มไปด้วยข้อผิดพลาดเล็กๆ น้อยๆ ประการที่สองควรให้ความสนใจกับตัวแปรด้วยตัวfinalแก้ไข เมื่อใช้Serializableพวกมันจะถูกซีเรียลไลซ์และดีซีเรียลไลซ์ตามปกติ แต่เมื่อใช้แล้ว เป็นไปไม่ได้ Externalizableที่จะดีซีเรียลไลซ์finalตัวแปร ! เหตุผลง่ายๆ คือ - finalฟิลด์ทั้งหมดจะถูกเตรียมใช้งานเมื่อมีการเรียกคอนสตรัคเตอร์เริ่มต้น และหลังจากนั้นค่าจะไม่สามารถเปลี่ยนแปลงได้ ดังนั้นในการซีเรียลไลซ์อ็อบเจ็กต์ที่มีfinal-fields ให้ใช้การทำให้ซีเรียลไลซ์มาตรฐานผ่านSerializable. ประการที่สามเมื่อใช้การสืบทอด คลาสที่สืบทอดทั้งหมดจากบางExternalizableคลาสจะต้องมีตัวสร้างเริ่มต้นด้วย ต่อไปนี้เป็นลิงก์ไปยังบทความดีๆ เกี่ยวกับกลไกการทำให้เป็นอนุกรม: พบกันใหม่! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION