บทความนี้อธิบายถึงการทำให้เป็นอนุกรมของวัตถุและ deserialization ใน Java แบ่งปันสำหรับทุกคนสำหรับการอ้างอิงของคุณ รายละเอียดมีดังนี้:
1. บทนำ
การทำให้เป็นอนุกรมวัตถุหมายถึงกระบวนการแปลงวัตถุเป็นลำดับของไบต์ในขณะที่ deserialization เป็นกระบวนการของการกู้คืนวัตถุตามลำดับของไบต์
การทำให้เป็นอนุกรมโดยทั่วไปจะใช้ในสถานการณ์ต่อไปนี้:
1. บันทึกวัตถุอย่างถาวรและบันทึกลำดับไบต์ของวัตถุไปยังไฟล์ท้องถิ่น
2. ผ่านวัตถุในเครือข่ายโดยการทำให้เป็นอนุกรม
3. ผ่านวัตถุระหว่างกระบวนการผ่านการทำให้เป็นอนุกรม
คลาสที่วัตถุเป็นของจะต้องใช้อินเทอร์เฟซ serializable หรือ externalizable ที่จะเป็นอนุกรม สำหรับคลาสที่ใช้อินเทอร์เฟซอนุกรมการทำให้เป็นอนุกรมและการทำให้เป็นอนุกรมใช้วิธีการทำให้เป็นอนุกรมเริ่มต้น
java.io.ObjectOutputStream แสดงถึงกระแสเอาต์พุตของวัตถุและวิธีการ writeObject (Object OBJ) สามารถตระหนักถึงการทำให้เป็นอนุกรมของวัตถุและเขียนลำดับไบต์ที่ได้รับไปยังสตรีมเอาต์พุตเป้าหมาย
java.io.ObjectInputStream แสดงถึงสตรีมอินพุตวัตถุและวิธีการ readObject () ของมันสามารถอ่านลำดับของไบต์จากสตรีมอินพุตแหล่งที่มา deserialize มันลงในวัตถุและส่งคืน
2. หลายวิธีในการทำให้เป็นอนุกรม
สมมติว่ามีการกำหนดคลาสลูกค้าขึ้นอยู่กับวิธีการทำให้เป็นอนุกรมของลูกค้าอาจมีวิธีการทำให้เป็นอนุกรมต่อไปนี้:
1. ใช้วิธี readObject และ writeObject ที่ไม่ได้กำหนด
ObjectOutputStream ใช้ JDK เพื่อทำให้เป็นอนุกรมตัวแปรอินสแตนซ์ที่ไม่ใช่การขนส่งของวัตถุลูกค้าโดยค่าเริ่มต้น
ObjectInputStream deserializes ตัวแปรอินสแตนซ์ที่ไม่เปลี่ยนแปลงของวัตถุลูกค้าโดยใช้วิธีเริ่มต้น JDK
2. ใช้วิธีการแบบ serializable และกำหนดวิธีการอ่านและการเขียน
ObjectOutputStream เรียกใช้วิธีการเขียน (ObjectOutputStream) ของคลาสลูกค้าเพื่อทำให้เป็นอนุกรมตัวแปรอินสแตนซ์ที่ไม่ใช่การขนส่งของวัตถุลูกค้า
ObjectInputStream เรียกใช้วิธี readObject (ObjectInputStream ใน) ของคลาสลูกค้าเพื่อ deserialize ตัวแปรอินสแตนซ์ที่ไม่ใช่การขนส่งของวัตถุลูกค้า
3. ใช้วิธีการภายนอกกำหนดวิธีการอ่านและวิธีการ writeexternal
ObjectOutputStream เรียกใช้วิธีการ writeExternal ของคลาสลูกค้าเพื่อทำให้เป็นอนุกรมตัวแปรอินสแตนซ์ที่ไม่ใช่การขนส่งของวัตถุลูกค้า
ObjectInputStream เป็นครั้งแรกที่สร้างวัตถุผ่านตัวสร้างพารามิเตอร์ที่ไม่มีพารามิเตอร์ของคลาสลูกค้าจากนั้น deserializes ตัวแปรอินสแตนซ์ที่ไม่เปลี่ยนแปลงของวัตถุลูกค้าโดยใช้วิธีการอ่าน
3. อินเตอร์เฟส serializable
คลาสเปิดใช้งานฟังก์ชันการทำให้เป็นอนุกรมโดยใช้อินเตอร์เฟส java.io.serializable คลาสที่ไม่ได้ใช้อินเทอร์เฟซนี้จะไม่สามารถทำให้เป็นอนุกรมหรือ deserialize ของรัฐใด ๆ ชนิดย่อยทั้งหมดของคลาส serializable นั้นเป็นแบบอนุกรม อินเทอร์เฟซอนุกรมไม่มีวิธีการหรือฟิลด์และใช้เพื่อระบุความหมายแบบอนุกรมเท่านั้น
ในระหว่างกระบวนการ deserialization ฟิลด์ของคลาสที่ไม่ได้รับการกำหนดจะเริ่มต้นโดยใช้ตัวสร้างพารามิเตอร์ทั่วไปหรือป้องกันของคลาส subclass serializable จะต้องสามารถเข้าถึงตัวสร้างแบบไม่มีพารามิเตอร์ ฟิลด์ที่สามารถเป็น subclasses serializable จะได้รับการกู้คืนจากสตรีม
เมื่อข้ามมุมมองคลาสคุณอาจพบวัตถุที่ไม่รองรับอินเตอร์เฟส serializable ในกรณีนี้จะมีการโยนแบบไม่ใช้งานได้และชั้นเรียนที่ไม่สามารถระบุได้แบบอนุกรม
1. ลายเซ็นที่ถูกต้อง
คลาสที่ต้องใช้การประมวลผลพิเศษในระหว่างการทำให้เป็นอนุกรมและ deserialization จะต้องใช้วิธีการพิเศษโดยใช้ลายเซ็นที่แม่นยำต่อไปนี้:
โมฆะส่วนตัว writeObject (java.io.ObjectOutputStream ออก) โยน ioexception
โมฆะส่วนตัว readObject (java.io.ObjectInputStream ใน) พ่น IOException, classnotFoundException;
โมฆะส่วนตัว readobjectNodata () พ่น ObjectStreamexception;
วิธี WriteObject มีหน้าที่ในการเขียนสถานะของวัตถุของคลาสที่เฉพาะเจาะจงเพื่อให้วิธี readObject ที่สอดคล้องกันสามารถกู้คืนได้ กลไกเริ่มต้นสำหรับการบันทึกฟิลด์ของวัตถุสามารถเรียกได้โดยการโทรออก defaultWriteObject วิธีการเองไม่จำเป็นต้องเกี่ยวข้องกับรัฐที่เป็นของซูเปอร์คลาสหรือคลาสย่อย สถานะสามารถบันทึกได้โดยการเขียนแต่ละฟิลด์ลงใน ObjectOutputStream โดยใช้วิธีการเขียนหรือใช้วิธีการที่สนับสนุนโดย dataOutput สำหรับประเภทข้อมูลพื้นฐาน
วิธี ReadObject มีหน้าที่ในการอ่านและกู้คืนฟิลด์คลาสจากสตรีม มันสามารถเรียก In.defaultreadObject เพื่อเรียกกลไกเริ่มต้นเพื่อกู้คืนฟิลด์ที่ไม่คงที่และไม่ใช่การขนส่งของวัตถุ วิธี defaultreadObject ใช้ข้อมูลในสตรีมเพื่อจัดสรรฟิลด์ของวัตถุในสตรีมที่บันทึกผ่านฟิลด์ที่ระบุที่เกี่ยวข้องในวัตถุปัจจุบัน สิ่งนี้ใช้เพื่อจัดการกับสถานการณ์ที่จำเป็นต้องเพิ่มฟิลด์ใหม่หลังจากวิวัฒนาการของชั้นเรียน วิธีการเองไม่จำเป็นต้องเกี่ยวข้องกับรัฐที่เป็นของซูเปอร์คลาสหรือคลาสย่อย สถานะสามารถบันทึกได้โดยการเขียนแต่ละฟิลด์ลงใน ObjectOutputStream โดยใช้วิธีการเขียนหรือใช้วิธีการที่สนับสนุนโดย dataOutput สำหรับประเภทข้อมูลพื้นฐาน
ในกรณีที่สตรีมการทำให้เป็นอนุกรมไม่ได้แสดงรายการคลาสที่กำหนดเป็น superclass ที่จะ deserialized วิธี readobjectNodata มีหน้าที่รับผิดชอบในการเริ่มต้นสถานะวัตถุของคลาสเฉพาะ สิ่งนี้เกิดขึ้นเมื่อคลาสอินสแตนซ์ deserialized ที่ใช้โดยตัวรับสัญญาณนั้นแตกต่างจากผู้ส่งและคลาสที่ขยายโดยรุ่นตัวรับสัญญาณไม่ได้เป็นคลาสที่ขยายโดยเวอร์ชันผู้ส่ง นอกจากนี้ยังเกิดขึ้นเมื่อสตรีมการทำให้เป็นอนุกรมได้รับการดัดแปลงด้วย
เมื่อเขียนวัตถุไปยังสตรีมคุณต้องระบุคลาส serializable ของวัตถุทางเลือกที่จะใช้และคุณควรใช้วิธีพิเศษนี้ด้วยลายเซ็นที่แม่นยำ:
Access-modifier Object WriteReplace () พ่น ObjectStreamexception;
วิธีการเขียนนี้จะถูกเรียกโดยการทำให้เป็นอนุกรมหากมีวิธีนี้และสามารถเข้าถึงได้โดยวิธีการที่กำหนดไว้ในคลาสของวัตถุที่เป็นอนุกรม ดังนั้นวิธีนี้สามารถมีการเข้าถึงแบบส่วนตัวได้รับการปกป้องและแพ็คเกจ การเข้าถึงคลาสย่อยของวิธีนี้เป็นไปตามกฎการเข้าถึง Java
เมื่ออ่านอินสแตนซ์ของคลาสจากสตรีมคุณต้องระบุลายเซ็นที่แน่นอนที่คลาสอื่นควรใช้เพื่อใช้วิธีพิเศษนี้
วัตถุใด ๆ ที่เข้าถึงได้ modifier readresolve () พ่น ObjectStreamexception;
วิธี readresolve นี้เป็นไปตามกฎการโทรและกฎการเข้าถึงเช่นเดียวกับ WriteReplace
หากชั้นเรียนกำหนดวิธี readResolve วิธีการ readResolve จะถูกเรียกในตอนท้ายของ deserialization และวัตถุที่ส่งคืนโดยวิธีนี้เป็นผลลัพธ์สุดท้ายของการ deserialization
2.SerialVersionuid
รันไทม์ Serialization ใช้หมายเลขเวอร์ชันที่เรียกว่า SerialVersionUID เพื่อเชื่อมโยงกับแต่ละคลาส serializable ซึ่งใช้ในระหว่างการ deserialization เพื่อตรวจสอบว่าผู้ส่งและตัวรับสัญญาณของวัตถุอนุกรมที่โหลดสำหรับวัตถุ หากตัวรับสัญญาณโหลด serialVersionUID ของคลาสของวัตถุที่แตกต่างจากหมายเลขเวอร์ชันของผู้ส่งที่สอดคล้องกันการ deserialization จะส่งผลให้เกิด invalidClassexception คลาส serializable สามารถประกาศ serialversionuid ของตัวเองได้อย่างชัดเจนโดยการประกาศฟิลด์ชื่อ "serialversionuid" (ฟิลด์นั้นจะต้องเป็นสนามยาวคงที่สุดท้าย):
ancess-modifier คงที่สุดท้าย serialversionuid = 42l;
หากคลาส serializable ไม่ได้ประกาศอย่างชัดเจน serialversionuid, runtime serialization จะคำนวณค่า serialversionuid เริ่มต้นของคลาสตามแง่มุมต่าง ๆ ของคลาสตามที่อธิบายไว้ใน "ข้อกำหนดการทำให้เป็นอนุกรมการกำหนดค่าวัตถุ Java (TM)" อย่างไรก็ตามขอแนะนำอย่างยิ่งว่าคลาส serializable ทั้งหมดจะประกาศค่า SerialVersionuid อย่างชัดเจนเนื่องจากการคำนวณ serialversionuid เริ่มต้นมีความไวสูงต่อรายละเอียดของชั้นเรียนและอาจแตกต่างกันอย่างมากขึ้นอยู่กับการใช้งานคอมไพเลอร์ดังนั้นในกระบวนการ deserialization อาจเป็นผล ดังนั้นเพื่อให้แน่ใจว่ามีความสอดคล้องระหว่างค่า SerialVersionUID ในคอมไพเลอร์ Java ที่แตกต่างกันคลาสที่เป็นอนุกรมจะต้องประกาศค่า SerialVersionUID ที่ชัดเจน ขอแนะนำอย่างยิ่งให้ใช้ตัวดัดแปลงส่วนตัวเพื่อแสดงการประกาศ serialVersionuid หากเป็นไปได้เนื่องจากการประกาศดังกล่าวใช้เพื่อประกาศคลาสโดยตรง - ฟิลด์ SerialVersionUID ในฐานะสมาชิกที่สืบทอดมานั้นไม่มีประโยชน์ คลาสอาร์เรย์ไม่สามารถประกาศ serialVersionUID ที่ชัดเจนดังนั้นพวกเขาจึงมีค่าเริ่มต้นที่คำนวณได้เสมอ แต่คลาสอาร์เรย์ไม่ตรงกับข้อกำหนดของค่า SerialVersionUID
3. อินเทอร์เฟซ externalizable
Externalizable เป็นส่วนขยายของ serailizable
โทรหาวิธีการของชั้นเรียน writeexternal ระหว่างการทำให้เป็นอนุกรมและ deserialize วิธีการอ่าน
เมื่อดำเนินการ deserialization ตัวสร้างพารามิเตอร์ของคลาสจะเรียกว่าแรกซึ่งแตกต่างจาก deserialization เริ่มต้น
ประการที่สี่สรุป
หากใช้วิธีการทำให้เป็นอนุกรมเริ่มต้นตราบใดที่คลาสใช้อินเทอร์เฟซแบบอนุกรมอินสแตนซ์ของมันสามารถทำให้เป็นอนุกรม โดยทั่วไปคลาสที่ออกแบบมาโดยเฉพาะสำหรับการสืบทอดควรพยายามที่จะไม่ใช้อินเตอร์เฟส serializable เพราะเมื่อคลาสแม่ใช้อินเทอร์เฟซ serializable ได้คลาสย่อยทั้งหมดของมันจะเป็น serializable
ข้อบกพร่องของวิธีการทำให้เป็นอนุกรมเริ่มต้น:
1. ไม่ปลอดภัยที่จะทำให้เป็นอนุกรมข้อมูลที่ละเอียดอ่อนโดยตรงซึ่งไม่เหมาะสำหรับการเปิดเผยวัตถุ
2. มันจะไม่ตรวจสอบว่าตัวแปรสมาชิกของวัตถุตรงกับข้อ จำกัด ที่ถูกต้องหรือไม่และอาจถูกดัดแปลงกับข้อมูลและทำให้เกิดการทำงานผิดปกติหรือไม่
3. จำเป็นต้องใช้กราฟวัตถุแบบเรียกซ้ำ
4. ทำให้อินเทอร์เฟซของคลาสถูก จำกัด โดยการใช้งานภายในของคลาสโดย จำกัด การอัพเกรดและการบำรุงรักษาของชั้นเรียน
โดยการใช้งานประเภทส่วนตัว writeObject () และ readObject () ของอินเตอร์เฟส serializable หรือการใช้อินเทอร์เฟซภายนอกที่สามารถใช้งานได้การใช้วิธีการ writeexternal () และวิธีการอ่าน () และการจัดทำโครงสร้างแบบไร้พารามิเตอร์แบบสาธารณะสองวิธี สามารถหลีกเลี่ยงข้อบกพร่องของวิธีการทำให้เป็นอนุกรมเริ่มต้นได้อย่างมีประสิทธิภาพ
หวังว่าบทความนี้จะเป็นประโยชน์กับการออกแบบโปรแกรม Java ของทุกคน