เหตุใด Java จึงต้องการอินเตอร์เฟสแบบอนุกรม


114

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

คำถามคือ: เนื่องจาก Serializable เป็นอินเทอร์เฟซที่ว่างเปล่าและ Java ให้ซีเรียลไลเซชั่นที่แข็งแกร่งเมื่อคุณเพิ่ม implements Serializable- ทำไมพวกเขาไม่ทำให้ทุกอย่างเป็นอนุกรมและนั่นล่ะ

ฉันขาดอะไรไป?


จะเกิดอะไรขึ้นถ้าคุณต้องการทำให้วัตถุของคุณสามารถต่ออนุกรมได้? หรือว่าฉันเข้าใจผิดอะไร?
Joe Phillips

ฉันจะยังคงได้รับ NotSerializableException เพราะทุกช่องของวัตถุของฉันต้องต่ออนุกรมกันได้
Yoni Roit

เห็นด้วยอย่างยิ่งกับ Pop Catalin และ Dmitry ของ "เคล็ดลับต่อเนื่องนี้เป็นเพียงการตัดสินใจที่ผิดพลาดอีกครั้งหนึ่งซึ่งเกิดขึ้นเมื่อทศวรรษหรือสองปีที่แล้ว" ไม่สำคัญว่าการทำให้เป็นอนุกรมจะอันตรายแค่ไหน และไม่เป็นความจริงที่ว่าเนื่องจากการประกาศนั้นชัดเจนแล้ว "คุณรู้ว่าคุณต้องใส่ใจเป็นพิเศษ" ทุกคนที่ต้องการสิ่งนี้จะต้องใส่ "อุปกรณ์" เป็นอันดับแรกจากนั้นจึงคิดเกี่ยวกับนัยหากมีสิ่งผิดปกติเกิดขึ้น อาจจะชัดเจนกว่านี้หากพวกเขาให้อินเทอร์เฟซ "ที่ไม่สามารถใช้งานได้" กับเราเพื่อใช้กับกรณีพิเศษ
แจ็ค

คำตอบ:


120

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

สำหรับการคงอยู่ในระยะยาวคลาสต้องสามารถถอดรหัสแบบฟอร์มนี้ได้ซึ่งจะ จำกัด การเปลี่ยนแปลงที่คุณสามารถทำได้ในการออกแบบคลาส สิ่งนี้ทำลายการห่อหุ้ม

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

ยังมีปัญหาอื่น ๆ เช่นรูปแบบคลาสภายในที่ไม่ได้รับการกำหนดไว้อย่างดี

การทำให้คลาสทั้งหมดสามารถต่ออนุกรมกันได้จะทำให้ปัญหาเหล่านี้แย่ลง ตรวจสอบที่มีประสิทธิภาพ Java Second Editionโดยเฉพาะอย่างยิ่งรายการ 74: ระบบ Serializable รอบคอบ


2
นี่เป็นคำตอบที่ดีกว่าอย่างชัดเจนฉันรู้สึกผิดหวังที่คำตอบที่เลือกไม่ใช่คำตอบนี้ดูเหมือนว่าผู้โพสต์จะเลือกโดยมีวาระ "ฉันรำคาญเพราะฉันต้องประกาศสิ่งต่างๆให้เป็นลำดับต่อเนื่องกัน" ในใจ เป็นตัวอย่างของคนบางคนที่ต้องการอิสระและไม่สนใจความปลอดภัยและการออกแบบบทเรียนที่ได้เรียนรู้ไปแล้วในอดีต
gbtimmon

@McDowell คุณหมายถึงอะไรจากรูปแบบการเรียนต่อเนื่อง? ฉันไปตามลิงค์นั้น แต่ไม่เข้าใจว่าคุณหมายถึงอะไร? คุณช่วยอธิบายเรื่องนี้ได้ไหม
Geek

@Geek - รูปแบบที่ทำให้เป็นอนุกรมของประเภท URL (ตัวอย่าง) เป็นตัวกำหนดฟิลด์ส่วนตัวที่ประเภทต้องมีและลำดับที่ต้องประกาศ
McDowell

@McDowell ทำไมออร์เดอร์ถึงสำคัญ
Geek

12
@McDowell สวัสดี ฉันเป็นผู้โพสต์ต้นฉบับของคำถามนี้เมื่อ 4 ปีที่แล้วและฉันเพิ่งเลือกคำตอบของคุณว่ายอมรับแล้วถ้ามันมีความหมายอะไร ฉันคิดว่าคำตอบของคุณเป็นคำตอบที่ดีกว่าจริงๆและตอนนั้นฉันอาจจะยังไม่บรรลุนิติภาวะเกินไปที่จะเห็นแบบนั้น กำลังแก้ไขอยู่ :)
Yoni Roit

32

ฉันคิดว่าทั้ง Java และ. Net คนเข้าใจผิดในครั้งนี้น่าจะดีกว่าที่จะทำให้ทุกอย่างเป็นอนุกรมตามค่าเริ่มต้นและต้องทำเครื่องหมายเฉพาะคลาสที่ไม่สามารถต่ออนุกรมได้อย่างปลอดภัยแทน

ตัวอย่างเช่นใน Smalltalk (ภาษาที่สร้างขึ้นในยุค 70) ทุกออบเจ็กต์สามารถต่ออนุกรมกันได้ตามค่าเริ่มต้น ฉันไม่รู้ว่าเหตุใดจึงไม่เป็นเช่นนั้นใน Java โดยพิจารณาจากข้อเท็จจริงที่ว่าวัตถุส่วนใหญ่ปลอดภัยที่จะทำให้เป็นอนุกรมและมีเพียงไม่กี่ชิ้นเท่านั้น

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

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


26
คุณต้องใช้ความระมัดระวังเป็นพิเศษเมื่อออกแบบและใช้งานคลาสเพื่อให้แน่ใจว่าอินสแตนซ์จะได้รับการจัดลำดับอย่างเหมาะสม จริงๆแล้วอินเทอร์เฟซที่ต่ออนุกรมได้หมายถึง: "ฉันในฐานะโปรแกรมเมอร์ได้เข้าใจผลของการทำให้เป็นอนุกรมและอนุญาตให้ JVM ทำให้เป็นอนุกรมได้"
Rolf Rander

@Rolf Rander: ส่วนใหญ่แล้วคุณไม่จำเป็นต้องดูแลใด ๆ เลยเพียงแค่ทำเครื่องหมายคลาสที่เป็นอนุกรมได้ ถ้าการทำให้เป็นอนุกรมจะถูกเปิดโดย defaul บนวัตถุทั้งหมดความคิดของนักพัฒนาทุกคนก็จะแตกต่างกันเช่นกันมันคงเป็นเรื่องธรรมดาที่จะทำให้คลาสอนุกรมกันได้ ...
Pop Catalin

มันจะเพิ่มความกังวลอีกอย่างในรายการการออกแบบคลาสที่ดีเช่นตรวจสอบให้แน่ใจว่าคุณไม่มีหน่วยความจำรั่วไหล ถึงกระนั้นการออกแบบชั้นเรียนที่ดีก็ต้องการให้ชั้นเรียนของคุณสามารถต่อเนื่องได้มากขึ้นเมื่อสามารถทำได้
Pop Catalin

3
@StaxMan เนื่องจากการตระหนักในภายหลังว่าคุณจำเป็นต้องทำให้คลาสเป็นลำดับและคุณทำไม่ได้อาจมีค่าใช้จ่ายสูงมาก เป็นหนึ่งในกรณีที่จ่ายเงินเพิ่มเพียงเล็กน้อย โดยเฉพาะอย่างยิ่งหากคุณกำลังเขียนห้องสมุดและคนอื่นจะใช้มันโดยไม่มีซอร์สโค้ด
Pop Catalin

2
ฉันเห็นด้วยอย่างยิ่งกับคำตอบนี้ ในหลายภาษาทุกอย่างสามารถต่ออนุกรมได้ตามค่าเริ่มต้น และเหมือนกันกับ JVM เพราะง่ายต่อการใช้ Reflection เพื่อเข้าถึงสมาชิกคลาสใด ๆ ไม่ว่าจะเป็นแบบส่วนตัวหรือไม่ก็ตาม Serializableเคล็ดลับนี้เป็นเพียงการตัดสินใจที่ผิดอีกครั้งหนึ่งซึ่งเกิดขึ้นเมื่อทศวรรษหรือสองปีที่แล้วและอีกอย่างหนึ่งที่ทำให้รำคาญเมื่อจัดการกับ java แท้เช่นข้อบกพร่องบางประการในการรวบรวมและการประมวลผลสตริงในไลบรารีมาตรฐาน มี Kryo อย่างมีความสุข แต่มันเป็นที่พึ่งพาและใคร ๆ ก็ต้องหามันก่อน นี่คือวิธีการทำให้เป็นอนุกรมในตัว
dmitry

20

ไม่ใช่ทุกอย่างที่สามารถต่อเนื่องกันได้อย่างแท้จริง ใช้การเชื่อมต่อซ็อกเก็ตเครือข่ายเช่น คุณสามารถทำให้ข้อมูล / สถานะของออบเจ็กต์ซ็อกเก็ตของคุณเป็นอนุกรมได้ แต่สาระสำคัญของการเชื่อมต่อที่ใช้งานอยู่จะหายไป


นี่จะเป็นปัญหาของฉันและถ้าฉันไม่ฉลาดพอที่จะพยายามทำให้ซ็อกเก็ตเป็นอนุกรมฉันจะพบข้อผิดพลาดระหว่างการดีบัก อย่างไรก็ตามตอนนี้ฉันตกอยู่ในสถานการณ์ที่ไม่สามารถใช้ Java serialization ได้เลยเนื่องจากคลาสของบุคคลที่สามซึ่งไม่ได้ใช้ Serializable โดยไม่มีเหตุผลที่ดี
โยนีรากษส

4
มีหลายวิธีที่ดีในการจัดการกรณีนี้เช่นการเขียนคลาสกระดาษห่อแบบอนุกรมที่รู้วิธีอ่านและเขียนข้อมูลสำคัญของคลาสของบุคคลที่สาม ทำให้อินสแตนซ์ที่ห่อไว้ชั่วคราวและแทนที่ writeObject และ readObject
Greg Case

คุณสามารถสืบทอดจากอ็อบเจ็กต์ที่ต้องการใน api ของคุณและทำให้คลาสเหล่านั้นเป็นอนุกรมได้หรือไม่?
Joel Coehoorn

@ Joel: เป็นความคิดที่ดี แต่ก็ยังแฮ็ค ฉันเดาว่าเรื่องทั้งหมดนี้เป็นเพียงการแลกเปลี่ยนที่ผิดพลาดอีกครั้ง ขอบคุณสำหรับความคิดเห็นของคุณ
โยนีรอย

1
นี่เป็นหนึ่งในตัวอย่างที่หายากซึ่งแสดงให้เห็นว่าสิ่งที่เราต้องการจริงๆคือimplements NotSerializable:)
Rob Grant

13

บทบาทหลักของ Serializable ใน Java คือการสร้างวัตถุอื่น ๆ ทั้งหมดที่ไม่สามารถกำหนดค่าได้ตามค่าเริ่มต้น การทำให้เป็นอนุกรมเป็นกลไกที่อันตรายมากโดยเฉพาะอย่างยิ่งในการใช้งานเริ่มต้น ดังนั้นเช่นเดียวกับมิตรภาพใน C ++ มันจะถูกปิดโดยค่าเริ่มต้นแม้ว่าจะมีค่าใช้จ่ายเล็กน้อยในการทำให้สิ่งต่างๆต่อเนื่องกันได้

การทำให้เป็นอนุกรมเพิ่มข้อ จำกัด และปัญหาที่อาจเกิดขึ้นเนื่องจากความเข้ากันได้ของโครงสร้างไม่ได้รับการประกัน เป็นการดีที่จะปิดโดยปริยาย

ฉันต้องยอมรับว่าฉันได้เห็นคลาสที่ไม่สำคัญน้อยมากที่การทำให้อนุกรมมาตรฐานทำในสิ่งที่ฉันต้องการ โดยเฉพาะอย่างยิ่งในกรณีของโครงสร้างข้อมูลที่ซับซ้อน ดังนั้นความพยายามที่คุณจะใช้ในการทำให้คลาส Serializble นั้นถูกต้องทำให้เสียค่าใช้จ่ายในการเพิ่มส่วนต่อประสาน


9

สำหรับบางคลาสโดยเฉพาะอย่างยิ่งคลาสที่แสดงถึงสิ่งที่มีอยู่จริงเช่นไฟล์ซ็อกเก็ตเธรดหรือการเชื่อมต่อ DB มันไม่สมเหตุสมผลเลยที่จะทำให้อินสแตนซ์เป็นอนุกรม สำหรับคนอื่น ๆ การทำให้เป็นอนุกรมอาจเป็นปัญหาได้เพราะมันทำลายข้อ จำกัด ของความเป็นเอกลักษณ์หรือบังคับให้คุณจัดการกับอินสแตนซ์ของคลาสเวอร์ชันต่างๆซึ่งคุณอาจไม่ต้องการ

อาจจะเป็นการดีกว่าที่จะทำให้ทุกอย่างเป็นอนุกรมได้ตามค่าเริ่มต้นและทำให้ชั้นเรียนไม่ต่อเนื่องกันได้ผ่านอินเทอร์เฟซคำหลักหรือเครื่องหมาย - แต่ผู้ที่ควรใช้ตัวเลือกนั้นอาจจะไม่คิดเกี่ยวกับเรื่องนี้ ในทางที่เป็นอยู่หากคุณต้องการใช้ Serializable คุณจะได้รับแจ้งด้วย Exception


4

ฉันคิดว่าอย่างไรก็ตามเพื่อให้แน่ใจว่าคุณในฐานะโปรแกรมเมอร์รู้ว่าวัตถุของคุณถูกทำให้เป็นอนุกรม


3

เห็นได้ชัดว่าทุกอย่างสามารถต่อเนื่องกันได้ในการออกแบบเบื้องต้นบางอย่าง แต่เนื่องจากความปลอดภัยและความถูกต้องเกี่ยวข้องกับการออกแบบขั้นสุดท้ายจึงลงเอยอย่างที่เราทุกคนทราบ

ที่มา: เหตุใดคลาสจึงต้องใช้ Serializable เพื่อที่จะเขียนลงใน ObjectOutputStream .


1

ต้องระบุอย่างชัดเจนว่าอินสแตนซ์ของคลาสหนึ่ง ๆ สามารถต่ออนุกรมได้ภาษาจะบังคับให้คุณคิดว่าคุณควรอนุญาตหรือไม่ สำหรับการจัดลำดับวัตถุที่มีค่าอย่างง่ายนั้นเป็นเรื่องเล็กน้อย แต่ในกรณีที่ซับซ้อนกว่านั้นคุณต้องคิดให้ดี

เพียงแค่อาศัยการสนับสนุนการทำให้เป็นอนุกรมมาตรฐานของ JVM คุณจะพบกับปัญหาการกำหนดเวอร์ชันที่น่ารังเกียจทุกประเภท

ความเป็นเอกลักษณ์การอ้างอิงทรัพยากร 'ของจริง' ตัวจับเวลาและสิ่งประดิษฐ์ประเภทอื่น ๆ อีกมากมายไม่ได้เป็นตัวเลือกสำหรับการทำให้เป็นอนุกรม


1

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

http://www.codingeek.com/java/io/object-streams-serialization-deserialization-java-example-serializable-interface/


0

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

ดังนั้นสิ่งที่คุณต้องทำโดยพื้นฐานคืออ่านคุณสมบัติทั้งหมดของคลาสบุคคลที่สามของคุณด้วยตัวคุณเอง หรือถ้าเป็นตัวเลือกสำหรับคุณ: ถอดรหัสให้ใส่คีย์เวิร์ดเหี้ยตรงนั้นแล้วคอมไพล์ใหม่


2
การทำให้เป็นอนุกรมเพิ่มข้อ จำกัด และปัญหาที่อาจเกิดขึ้นเนื่องจากความเข้ากันได้ของโครงสร้างไม่ได้รับการประกัน IMHO เป็นการดีที่จะปิดโดยค่าเริ่มต้น
Uri

ฉันไม่แน่ใจว่า "ความเข้ากันได้ของโครงสร้าง" หมายถึงอะไร
nes1983

0

มีบางอย่างใน Java ที่ไม่สามารถทำให้เป็นอนุกรมได้เนื่องจากเป็นรันไทม์เฉพาะ สิ่งต่างๆเช่นสตรีมเธรดรันไทม์ ฯลฯ และแม้แต่คลาส GUI บางคลาส (ซึ่งเชื่อมต่อกับ OS พื้นฐาน) ก็ไม่สามารถทำให้เป็นอนุกรมได้


0

แม้ว่าฉันจะเห็นด้วยกับประเด็นในคำตอบอื่น ๆ ที่นี่ แต่ปัญหาที่แท้จริงคือการ deserialisation: หากคำจำกัดความของคลาสเปลี่ยนไปก็มีความเสี่ยงอย่างแท้จริงที่การ deserialisation จะไม่ได้ผล การไม่แก้ไขฟิลด์ที่มีอยู่ถือเป็นความมุ่งมั่นที่สำคัญสำหรับผู้เขียนไลบรารี! การดูแลความเข้ากันได้ของ API นั้นเพียงพอสำหรับงานที่น่าเบื่อ


สิ่งนี้ไม่ถูกต้อง คุณต้องอ่านบทVersioning of Serializable Objectsของ Object Serialization Specification ซึ่งจะไม่มีอยู่จริงหากสิ่งที่คุณอ้างในที่นี้เป็นความจริง นิยามคลาสสามารถเปลี่ยนแปลงได้ภายในขีด จำกัด ที่ค่อนข้างกว้างก่อนที่จะไม่เข้ากันกับการทำให้เป็นอนุกรมก่อนหน้านี้ และมันไม่ใช่คำตอบสำหรับคำถามนี้อย่างแน่นอน เหตุผลที่แท้จริงเกี่ยวข้องกับปัญหาด้านความปลอดภัย
Marquis of Lorne

@EJP เพื่อประโยชน์ของความจริงฉันแก้ไขคำตอบของฉันเพื่อสะท้อนประเด็นของคุณ แม้ว่าความเชื่อมั่นจะยืนอยู่ แต่มันเป็นความมุ่งมั่นที่ยิ่งใหญ่ที่ควรเลือกเข้าร่วมแทนที่จะเลือกไม่ใช้
CurtainDog

0

คลาสที่จำเป็นต้องคงอยู่กับไฟล์หรือสื่ออื่น ๆ จะต้องใช้อินเทอร์เฟซแบบอนุกรมเพื่อให้ JVM สามารถอนุญาตให้คลาสอ็อบเจ็กต์เป็นอนุกรมได้ ทำไมคลาสอ็อบเจ็กต์จึงไม่เป็นอนุกรมแล้วไม่มีคลาสใดที่จำเป็นต้องใช้อินเทอร์เฟซหลังจากที่ JVM ทั้งหมดทำให้คลาสอนุกรมกันก็ต่อเมื่อฉันใช้ObjectOutputStreamซึ่งหมายความว่าการควบคุมยังอยู่ในมือของฉันเพื่อให้ JVM ทำการซีเรียลไลซ์

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

หากไม่ได้ระบุserialVersionUIDเราจะได้ผลลัพธ์ที่ไม่คาดคิดในขณะที่คลายการเชื่อมต่อวัตถุนั่นคือสาเหตุที่ JVM แสดงInvalidClassExceptionหากserialVersionUID ไม่ตรงกัน ดังนั้นทุกคลาสจึงต้องใช้อินเทอร์เฟซแบบอนุกรมและจัดเตรียมserialVersionUIDเพื่อให้แน่ใจว่าคลาสที่นำเสนอที่ปลายทั้งสองข้างนั้นเหมือนกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.