ทำไมต้องสร้าง serialVersionUID แบบยาวแทนที่จะเป็น 1L แบบง่าย?


210

เมื่อการดำเนินการระดับ Serializable ใน Eclipse ผมมีสองตัวเลือก: เริ่มต้นการเพิ่มหรือสร้างserialVersionUID(1L) serialVersionUID(3567653491060394677L)ฉันคิดว่าอันแรกเย็นกว่า แต่หลายครั้งฉันเห็นคนใช้ตัวเลือกที่สอง มีเหตุผลใดที่จะสร้างlong serialVersionUID?


49
ซ้ำกันอย่างแน่นอนเป็นอย่างไร ฉันไม่ถามทำไมสร้างเลย แต่ทำไมสร้าง serialVersionUID ยาว
IAdapter

13
เมื่อ Jon Skeet ใช้ serialVersionUID เขาจะใช้ 0L: stackoverflow.com/questions/605828/
......

8
@HannoFietz: ประโยคที่แน่นอนคือ: "สำหรับความเรียบง่ายฉันขอแนะนำให้เริ่มต้นด้วย 0 และเพิ่มขึ้นทีละ 1ทุกครั้งที่คุณต้องการ" ดังนั้นดูเหมือนว่าเขาจะใช้0Lในตอนแรกเท่านั้น
หรือผู้ทำแผนที่

7
@ ปกติ: คุณหมายความว่า Jon Skeet ต้องการย้อนกลับและอัปเดตรหัสที่เขาเขียนหรือไม่? แม้กระทั่งถึงจุดที่ไม่เข้ากันของโครงสร้าง อ้าปากค้าง! บาป!
Thilo

คำตอบ:


90

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

ดูJava Specization Specสำหรับรายละเอียดเพิ่มเติม


69

วัตถุประสงค์ของการทำให้เป็นอนุกรมรุ่น UID คือการติดตามรุ่นต่าง ๆ ของคลาสเพื่อดำเนินการเป็นอนุกรมของวัตถุที่ถูกต้อง

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

ใช้ ID เดียวกันเสมอเช่น1Lหมายความว่าในอนาคตหากมีการเปลี่ยนแปลงคำจำกัดความของคลาสซึ่งทำให้เกิดการเปลี่ยนแปลงโครงสร้างของวัตถุที่เป็นอนุกรมจะมีโอกาสที่ดีที่ปัญหาเกิดขึ้นเมื่อพยายามที่จะทำการ deserialize วัตถุ

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

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


50
แนวคิดเบื้องหลังการใช้ 1L คือเพื่อให้คุณเพิ่มขึ้นทุกครั้งที่คุณเปลี่ยนคุณสมบัติคลาสหรือเมธอด
Powerlord

39
ไม่มีผลกระทบต่อประสิทธิภาพการทำงานของการอนุญาตให้ serialversionUID ถูกสร้างขึ้นโดยอัตโนมัติ - มันถูกสร้างขึ้นตามเวลาที่รวบรวมโดย javac ... หากคุณทำการถอดรหัส bytecode ของคลาสคุณจะเห็นตัวแปรแบบคงที่ใน bytecode
Jared

11
หมายเหตุเพิ่มเติมอีกหนึ่งข้อ - ด้วยการจัดการหมายเลขอย่างชัดเจนคุณจะต้องตัดสินใจว่าเมื่อใดที่คุณพิจารณารุ่นของ "คลาสที่ใช้งานร่วมกันได้" แทนการกำหนดข้อกำหนดของคลาสให้เหมือนกันทุกประการ
Scott Stanchfield

23
@ Jared ตามรายการที่ 75 ในจาวาบลอคที่มีประสิทธิภาพของ Java: รุ่นที่ 2: "ประกาศ UID เวอร์ชันอนุกรมอย่างชัดเจนในทุก ๆ คลาสที่คุณเขียนได้ .... หากไม่มีการจัดหา UID เวอร์ชันอนุกรมการคำนวณราคาแพงจะต้องสร้างขึ้นมาหนึ่งรันไทม์ ."
โคลิน K

12
@coobird นี่น่าจะเป็นเหตุผลหลักว่าทำไมไม่แนะนำ serialVersionUID ที่เป็นค่าเริ่ม Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. ต้นความคิดเห็นข้างต้นถูกนำมาจาก Java Object Serialization Specification เวอร์ชั่น 6.0
user624558

20

เหตุผลหลักสำหรับสิ่งที่สร้างขึ้นคือเพื่อให้เข้ากันได้กับรุ่นที่มีอยู่ของคลาสที่มีอยู่แล้วคัดลอก


1
ตกลง แต่จะเหมือนกันถ้าฉันมี 1L เสมอ ทุกอย่างจะเข้ากันได้แม้ว่าฉันจะทำการเปลี่ยนแปลงใด ๆ
grep

@grep ลองเปลี่ยนชื่อฟิลด์แล้วดูว่าเกิดอะไรขึ้น
Trejkaz

1
@ รับจุดคือถ้าคุณมีคลาสที่ละเว้น serialVersionUID มาก่อนมันจะได้รับที่สร้างขึ้นโดยอัตโนมัติ ดังนั้นตอนนี้คุณต้องการเริ่มการตั้งค่าอย่างชัดเจนการตั้งค่าเป็น 1L จะทำให้มันเข้ากันไม่ได้กับคลาสที่มีอยู่ในขณะที่การใช้ค่าที่สร้างขึ้นทำให้เข้ากันได้
marc82ch

15

ค่าดีฟอลต์ "long" ของserialVersionUIDคือค่าดีฟอลต์ตามที่กำหนดโดยข้อกำหนดคุณสมบัติการทำให้เป็นอนุกรมของJavaซึ่งคำนวณจากพฤติกรรมการทำอนุกรมที่เป็นค่าเริ่มต้น

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

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


11

อย่างคุณควรสร้าง serialVersionUID java.io.Serializableทุกครั้งที่คุณกำหนดคลาสที่นำไปปฏิบัติ หากคุณไม่สร้างขึ้นหนึ่งรายการจะถูกสร้างขึ้นโดยอัตโนมัติ แต่ไม่ดี serialVersionUID ที่สร้างขึ้นโดยอัตโนมัตินั้นขึ้นอยู่กับลายเซ็นของเมธอดของคลาสของคุณดังนั้นหากคุณเปลี่ยนคลาสของคุณในอนาคตเพื่อเพิ่มเมธอด (ตัวอย่างเช่น) การยกเลิกคลาส deserializing เวอร์ชัน "เก่า" ของคลาสจะล้มเหลว นี่คือสิ่งที่สามารถเกิดขึ้นได้:

  1. สร้างเวอร์ชันแรกของคลาสของคุณโดยไม่กำหนด serialVersionUID
  2. ทำให้อินสแตนซ์ของคลาสของคุณเป็นแบบเก็บถาวร serialVersionUID จะถูกสร้างขึ้นโดยอัตโนมัติสำหรับคุณ
  3. ปรับเปลี่ยนคลาสของคุณเพื่อเพิ่มวิธีการใหม่และปรับใช้แอปพลิเคชันของคุณใหม่
  4. ความพยายามที่จะทำการ deserialize อินสแตนซ์ที่ถูกทำให้เป็นอนุกรมในขั้นตอนที่ 2 แต่ตอนนี้มันล้มเหลว (เมื่อมันควรจะประสบความสำเร็จ) เพราะมันมี serialVersionUID ที่สร้างขึ้นโดยอัตโนมัติอื่น

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

6

หากคุณไม่ได้ระบุ serialVersionUID แล้ว Java จะทำให้เป็นเรื่องง่าย serialVersionUID ที่สร้างขึ้นคือหมายเลขนั้น หากคุณเปลี่ยนบางสิ่งบางอย่างในชั้นเรียนที่ไม่ได้ทำให้คลาสของคุณเข้ากันไม่ได้กับรูปแบบที่เป็นอนุกรมก่อนหน้านี้ แต่เปลี่ยนแฮชคุณต้องใช้หมายเลข serialVersionUID ที่สร้างขึ้นมาจำนวนมาก . มิฉะนั้นหากคุณกำลังติดตามทุกอย่างด้วยตัวเอง 0, 1, 2 ... จะดีกว่า


คุณหมายถึง ==> 1. ถ้าคุณต้องการให้คลาสที่เปลี่ยนแปลงต่างกันเข้ากันได้ให้ใช้คลาสที่สร้างขึ้น 2. ถ้าคุณต้องการให้คลาสที่แตกต่างกันของคลาสที่เข้ากันไม่ได้ให้ใช้ค่าเริ่มต้นและระมัดระวังในการเพิ่ม ฉันเข้าใจถูกต้องหรือไม่
JavaDeveloper

4

เมื่อคุณใช้ serialVersionUID (1L) แทนที่จะสร้าง serialVersionUID (3567653491060394677L) คุณกำลังพูดอะไรบางอย่าง

คุณกำลังบอกว่าคุณมั่นใจ 100% ว่าไม่มีระบบใดที่จะสัมผัสคลาสนี้ที่มีรุ่นที่ต่อเนื่องกันไม่ได้ของคลาสนี้ด้วยหมายเลขเวอร์ชัน 1

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

คุณสามารถทนทุกข์ทรมานมากกว่านั้น หรือคุณสามารถเล่นลอตเตอรี่ที่หวังว่าจะสูญเสีย หากคุณสร้างเวอร์ชันคุณมีโอกาสเล็กน้อยที่จะเกิดความผิดพลาด หากคุณถือว่า "เฮ้ฉันเดิมพันว่ายังไม่มีใครใช้ 1 เลย" อัตราต่อรองของคุณใหญ่กว่าเล็ก เป็นเพราะเราทุกคนคิดว่า 0 และ 1 นั้นยอดเยี่ยมที่คุณมีโอกาสสูงกว่าที่จะชนพวกเขา

-

เมื่อคุณสร้าง serialVersionUID (3567653491060394677L) แทนที่จะใช้ serialVersionUID (1L) คุณกำลังพูดอะไรบางอย่าง

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

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

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

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

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

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


หาก "ระบบ" สัมผัสกับคลาสเช่นเปลี่ยนคลาสในแบบที่ทำให้อนุกรมไม่เข้ากันคำถามก็คือถ้าระบบนั้นจะเปลี่ยน serialVersionUID ฉันไม่คิดว่าอัตราต่อรองจะเล็กลงเท่าไรซึ่งจะจำได้ว่าต้องเปลี่ยนเมื่อมีความยาว ฉันคิดว่าสิ่งที่ตรงกันข้ามนั้นเป็นจริงถ้าจำนวนที่ง่ายต่อการจดจำการเปลี่ยนแปลงนั้นสูงกว่าที่ฉันสังเกตเห็นว่าฉันไม่ได้ตั้งใจเปลี่ยน
Reto Gmür

2
นี่มันผิด! เมื่อคุณสร้าง serialVersionUID และประกาศค่านั้นในซอร์สโค้ดของคุณแทนที่จะเป็น 1L หรือไม่มีอะไรที่คุณพูดจริง ๆ : ฉันต้องการการชนที่ไม่ถูกตรวจจับในอนาคตด้วยเอฟเฟกต์ที่ไม่ได้กำหนดและฉันไม่ต้องการจาวาหรือมนุษย์เพื่อป้องกัน . Java เป็นหวาดระแวง แต่เชื่อฟัง โดยปกติแล้วมนุษย์จะไม่ยุ่งกับคนจำนวนมาก ด้วยวิธีนี้เมื่อชั้นเรียนมีการเปลี่ยนแปลงจาวายังคงสามารถยกเลิกการทำให้เป็นอนุกรมรุ่นที่เข้ากันไม่ได้ของมัน MwoaHaHa ... ;)
Superole

1

ทีนี้ serialVersionUID เป็นข้อยกเว้นสำหรับกฎที่ว่า“ สแตติกฟิลด์ไม่ได้รับการทำให้เป็นอนุกรม” ObjectOutputStream เขียนทุกครั้งที่ค่าของ serialVersionUID ไปยังเอาต์พุตสตรีม ObjectInputStream อ่านมันกลับมาและถ้าค่าที่อ่านจากกระแสไม่เห็นด้วยกับค่า serialVersionUID ในรุ่นปัจจุบันของชั้นเรียนแล้วมันจะโยน InvalidClassException ยิ่งกว่านั้นถ้าไม่มี serialVersionUID ที่ประกาศอย่างเป็นทางการในคลาสที่จะทำให้เป็นอนุกรมคอมไพเลอร์จะเพิ่มโดยอัตโนมัติด้วยค่าที่สร้างขึ้นตามฟิลด์ที่ประกาศในคลาส


0

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


คุณสามารถแก้ไขคำตอบของคุณเพื่อแก้ไขเพิ่มเติมได้หรือไม่? ดูเหมือนว่าความคิดเห็นที่นี่ ขอขอบคุณ
สีเทา

0

หากต้องการเพิ่มคำตอบ @David Schmitts เนื่องจากกฎง่ายๆฉันมักจะใช้ 1L ออกจากการประชุม ฉันต้องย้อนกลับไปและเปลี่ยนแปลงบางอย่างในสองสามครั้ง แต่ฉันรู้ว่าเมื่อฉันทำการเปลี่ยนแปลงและอัปเดตหมายเลขเริ่มต้นทีละรายการ

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


0

วัตถุประสงค์ของการทำให้เป็นอนุกรมรุ่น UID คือการติดตามรุ่นต่าง ๆ ของคลาสเพื่อดำเนินการเป็นอนุกรมของวัตถุที่ถูกต้อง

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

คำอธิบายง่ายๆ:

คุณซีเรียลไลซ์ข้อมูลหรือไม่

การทำให้เป็นอนุกรมนั้นคือการเขียนข้อมูลคลาสไปยังไฟล์ / สตรีม / ฯลฯ การทำให้เป็นอนุกรมกำลังอ่านข้อมูลนั้นกลับไปที่คลาส

คุณตั้งใจจะไปผลิตหรือไม่?

หากคุณเป็นเพียงการทดสอบบางอย่างกับข้อมูลที่ไม่สำคัญ / ข้อมูลปลอมไม่ต้องกังวลเกี่ยวกับมัน (ยกเว้นกรณีที่คุณกำลังทดสอบการทำให้เป็นอนุกรมโดยตรง)

นี่เป็นรุ่นแรกหรือไม่

ถ้าเป็นเช่นนั้นตั้ง serialVersionUID = 1L

นี่เป็นรุ่นที่สองสามและอื่น ๆ หรือไม่

ตอนนี้คุณต้องกังวลเกี่ยวกับ serialVersionUID และควรดูในเชิงลึก

โดยทั่วไปหากคุณไม่อัปเดตเวอร์ชันอย่างถูกต้องเมื่อคุณอัปเดตคลาสที่คุณต้องการเขียน / อ่านคุณจะได้รับข้อผิดพลาดเมื่อคุณพยายามอ่านข้อมูลเก่า

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