SerialVersionUID là gì và tại sao tôi nên sử dụng nó?


3000

Eclipse đưa ra cảnh báo khi serialVersionUIDthiếu.

Lớp tuần tự hóa Foo không khai báo trường serialVersionUID tĩnh cuối cùng loại dài

Là gì serialVersionUIDvà tại sao nó quan trọng? Vui lòng cho thấy một ví dụ nơi thiếu serialVersionUIDsẽ gây ra vấn đề.


1
Tìm một thực hành tốt về serialversionUID; dzone.com/articles/what-is-serialversionuid
ceyun

Câu trả lời:


2290

Các tài liệu cho java.io.Serializablecó lẽ là một lời giải thích tốt như bạn sẽ nhận được:

Thời gian chạy tuần tự hóa liên kết với mỗi lớp tuần tự hóa một số phiên bản, được gọi là a serialVersionUID, được sử dụng trong quá trình giải tuần tự hóa để xác minh rằng người gửi và người nhận của một đối tượng được tuần tự hóa đã tải các lớp cho đối tượng đó tương thích với tuần tự hóa. Nếu người nhận đã tải một lớp cho đối tượng có khác serialVersionUIDvới lớp của người gửi tương ứng, thì quá trình khử lưu huỳnh sẽ dẫn đến một InvalidClassException. Một lớp tuần tự hóa có thể khai báo serialVersionUIDrõ ràng bằng cách khai báo một trường có tên serialVersionUIDphải là tĩnh, cuối cùng và thuộc loại long:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

Nếu một lớp tuần tự hóa không khai báo rõ ràng a serialVersionUID, thì thời gian chạy tuần tự hóa sẽ tính toán một serialVersionUIDgiá trị mặc định cho lớp đó dựa trên các khía cạnh khác nhau của lớp, như được mô tả trong Đặc tả tuần tự hóa đối tượng Java (TM). Tuy nhiên, chúng tôi khuyến nghị tất cả các lớp tuần tự hóa khai báo rõ ràng serialVersionUIDcác giá trị, vì serialVersionUIDtính toán mặc định rất nhạy cảm với các chi tiết lớp có thể thay đổi tùy theo việc triển khai trình biên dịch và do đó có thể dẫn đến bất ngờ InvalidClassExceptionstrong quá trình khử lưu huỳnh. Do đó, để đảm bảo một serialVersionUIDgiá trị nhất quán trong các triển khai trình biên dịch java khác nhau, một lớp tuần tự hóa phải khai báo một serialVersionUIDgiá trị rõ ràng . Nó cũng được khuyến khích rằng rõ ràngserialVersionUIDcác khai báo sử dụng công cụ sửa đổi riêng nếu có thể, vì các khai báo đó chỉ áp dụng cho các trường khai báo ngay lập tức serialVersionUIDkhông hữu ích như các thành viên được kế thừa.


327
Vì vậy, những gì bạn đang nói về cơ bản là nếu người dùng không hiểu tất cả các tài liệu trên, cho biết người dùng không bận tâm về việc tuần tự hóa? Tôi tin rằng bạn đã trả lời "làm thế nào?" thay vì giải thích "tại sao?". Tôi, trước hết, không hiểu tại sao tôi lại bận tâm với serializableVersionUID.
Ziggy

365
Tại sao lại ở đoạn thứ hai: nếu bạn không chỉ định rõ ràng serialVersionUID, một giá trị được tạo tự động - nhưng điều đó dễ vỡ vì nó phụ thuộc vào trình biên dịch.
Jon Skeet

14
Và tại sao Eclipse nói rằng tôi cần "serialVersionUID tĩnh dài cuối cùng riêng tư = 1L;" khi tôi mở rộng lớp Exception?
JohnMerlino

21
@JohnMerlino: Chà tôi sẽ không nói rằng bạn cần một cái - nhưng nó có thể gợi ý một cái để giúp bạn tuần tự hóa các ngoại lệ một cách chính xác. Nếu bạn sẽ không tuần tự hóa chúng, bạn thực sự không cần hằng số.
Jon Skeet

16
@JohnMerlino, để trả lời lý do tại sao một phần câu hỏi của bạn: Ngoại lệ thực hiện Nối tiếp và nhật thực cảnh báo rằng bạn chưa đặt serialVersionUID, đó sẽ là một ý tưởng tốt (nếu bạn không tuần tự hóa lớp học) để tránh các vấn đề mà bài đăng của JonSkeet đề cương.
zpon

475

Nếu bạn tuần tự hóa chỉ vì bạn phải tuần tự hóa vì lợi ích của việc triển khai (ai quan tâm nếu bạn tuần tự hóa cho một HTTPSessionví dụ ... nếu nó được lưu trữ hay không, có lẽ bạn không quan tâm đến de-serializingmột đối tượng biểu mẫu), thì bạn có thể Bỏ qua điều này.

Nếu bạn thực sự sử dụng tuần tự hóa, nó chỉ quan trọng nếu bạn có kế hoạch lưu trữ và truy xuất các đối tượng bằng cách sử dụng tuần tự hóa trực tiếp. Các serialVersionUIDđại diện cho phiên bản lớp học của bạn, và bạn nên tăng nó nếu phiên bản hiện tại của lớp học của bạn không phải là tương thích ngược với phiên bản trước của nó.

Hầu hết thời gian, bạn có thể sẽ không sử dụng serialization trực tiếp. Nếu đây là trường hợp, tạo một mặc định SerialVersionUIDbằng cách nhấp vào tùy chọn sửa nhanh và đừng lo lắng về nó.


78
Tôi muốn nói rằng nếu bạn không sử dụng tuần tự hóa để lưu trữ vĩnh viễn, bạn nên sử dụng @SuppressWarnings thay vì thêm một giá trị. Nó làm giảm bớt lớp học và nó bảo vệ tính cơ bản của cơ chế serialVersionUID để bảo vệ bạn khỏi những thay đổi không tương thích.
Tom Anderson

25
Tôi không thấy cách thêm một dòng (chú thích @SuppressWarnings) trái ngược với dòng khác (id tuần tự hóa) "khóa lớp ít hơn". Và nếu bạn không sử dụng tuần tự hóa để lưu trữ vĩnh viễn, tại sao bạn không sử dụng "1"? Bạn sẽ không quan tâm đến ID được tạo tự động trong trường hợp đó.
MetroidFan2002

71
@ MetroidFan2002: Tôi nghĩ rằng quan điểm của @ TomAnderson serialVersionUIDbảo vệ bạn khỏi những thay đổi không tương thích là hợp lệ. Sử dụng @SuppressWarningstài liệu mục đích tốt hơn nếu bạn không muốn sử dụng lớp để lưu trữ vĩnh viễn.
AbdullahC

11
"Bạn nên tăng nó nếu phiên bản hiện tại của lớp của bạn không tương thích ngược với phiên bản trước của nó:" Trước tiên, bạn nên khám phá hỗ trợ phiên bản đối tượng mở rộng của Nối tiếp, (a) để đảm bảo rằng lớp thực sự hiện không tương thích nối tiếp, mà theo đặc điểm kỹ thuật là khá khó để đạt được; (b) để thử một lược đồ như các phương thức read / writeObject () tùy chỉnh, các phương thức readResolve / writeReplace (), khai báo serializableFields, v.v., để đảm bảo rằng luồng vẫn tương thích. Thay đổi thực tế serialVersionUIDlà một phương sách cuối cùng, một lời khuyên của tuyệt vọng.
Hầu tước Lorne

4
Gia tăng @EJP của serialVersionUID xuất hiện khi tác giả ban đầu của lớp đã giới thiệu rõ ràng. Tôi sẽ nói, jvm tạo id nối tiếp, sẽ ổn. đây là câu trả lời tốt nhất mà tôi thấy trên serialization.
trao đổi quá mức

307

Tôi không thể bỏ qua cơ hội này để cắm cuốn sách Java hiệu quả của Josh Bloch (Phiên bản 2) của . Chương 11 là một tài nguyên không thể thiếu trong tuần tự hóa Java.

Theo Josh, UID được tạo tự động được tạo dựa trên tên lớp, giao diện được triển khai và tất cả các thành viên được bảo vệ và công khai. Thay đổi bất kỳ trong số này theo bất kỳ cách nào sẽ thay đổiserialVersionUID . Vì vậy, bạn không cần phải gây rối với chúng chỉ khi bạn chắc chắn rằng không có nhiều hơn một phiên bản của lớp sẽ được tuần tự hóa (qua các quá trình hoặc được truy xuất từ ​​bộ lưu trữ sau đó).

Nếu bây giờ bạn bỏ qua chúng và thấy rằng bạn cần thay đổi lớp theo một cách nào đó nhưng vẫn duy trì khả năng tương thích với phiên bản cũ của lớp, bạn có thể sử dụng công cụ nối tiếp công cụ JDK để tạo lớp serialVersionUIDtrên lớp và đặt rõ ràng rằng vào lớp mới. (Tùy thuộc vào các thay đổi của bạn, bạn cũng có thể cần phải thực hiện tuần tự hóa tùy chỉnh bằng cách thêm writeObjectreadObjectphương thức - xem Serializablejavadoc hoặc chương 11. đã nói ở trên).


33
Vì vậy, người ta có thể bận tâm với serializableVersionUID nếu ai đó quan tâm đến khả năng tương thích với các phiên bản cũ của một lớp?
Ziggy

10
Yup, trong trường hợp nếu phiên bản mới hơn thay đổi bất kỳ thành viên nào thành được bảo vệ, thì SerializableVersionUID mặc định sẽ khác và sẽ đưa ra một UnlimitedClassExceptions.
Chander Shivdasani

3
Tên lớp, giao diện được triển khai, tất cả các phương thức công khai và được bảo vệ, TẤT CẢ các biến thể hiện.
Đạt

31
Điều đáng chú ý là Joshua Bloch khuyên rằng với mỗi lớp Nối tiếp, đáng để chỉ định uid phiên bản nối tiếp. Trích dẫn từ chương 11: Bất kể bạn chọn hình thức tuần tự nào, hãy khai báo UID phiên bản nối tiếp rõ ràng trong mỗi lớp tuần tự hóa mà bạn viết. Điều này loại bỏ UID phiên bản nối tiếp như là một nguồn không tương thích tiềm năng (Mục 74). Ngoài ra còn có một lợi ích hiệu suất nhỏ. Nếu không có UID phiên bản nối tiếp nào được cung cấp, cần phải tính toán đắt tiền để tạo một phiên bản khi chạy.
Ashutosh Jindal

136

Bạn có thể yêu cầu Eclipse bỏ qua các cảnh báo serialVersionUID này:

Cửa sổ> Tùy chọn> Java> Trình biên dịch> Lỗi / Cảnh báo> Sự cố lập trình tiềm năng

Trong trường hợp bạn không biết, có rất nhiều cảnh báo khác bạn có thể kích hoạt trong phần này (hoặc thậm chí có một số báo cáo là lỗi), rất nhiều cảnh báo rất hữu ích:

  • Các vấn đề về lập trình tiềm năng: Có thể có sự phân công boolean tình cờ
  • Vấn đề lập trình tiềm năng: Truy cập con trỏ Null
  • Mã không cần thiết: Biến cục bộ không bao giờ được đọc
  • Mã không cần thiết: Kiểm tra null dự phòng
  • Mã không cần thiết: Truyền không cần thiết hoặc 'instanceof'

và nhiều thứ khác nữa.


21
upvote nhưng chỉ bởi vì các poster ban đầu không xuất hiện để tuần tự hóa bất cứ điều gì. Nếu người đăng nói "Tôi đang đăng tuần tự thứ này và ..." thì bạn sẽ nhận được một phiếu bầu thay thế: P
John Gardner

3
Đúng - Tôi đã cho rằng người hỏi đơn giản là không muốn bị cảnh cáo
matt b

14
@Gardner -> đồng ý! Nhưng người hỏi cũng muốn biết tại sao anh ta có thể không muốn bị cảnh cáo.
Ziggy

8
Người hỏi rõ ràng quan tâm tại sao nên có UID. Vì vậy, chỉ cần nói với anh ta để bỏ qua cảnh báo nên được hạ cấp.
cinqS

111

serialVersionUIDtạo điều kiện cho phiên bản của dữ liệu nối tiếp. Giá trị của nó được lưu trữ cùng với dữ liệu khi tuần tự hóa. Khi hủy tuần tự hóa, cùng một phiên bản được kiểm tra để xem cách dữ liệu nối tiếp khớp với mã hiện tại.

Nếu bạn muốn phiên bản dữ liệu của mình, bạn thường bắt đầu bằng serialVersionUID0 và kết hợp nó với mọi thay đổi cấu trúc đối với lớp của bạn để thay đổi dữ liệu được tuần tự hóa (thêm hoặc xóa các trường không tạm thời).

Cơ chế khử tuần tự hóa tích hợp ( in.defaultReadObject()) sẽ từ chối khử tuần tự hóa từ các phiên bản cũ của dữ liệu. Nhưng nếu bạn muốn, bạn có thể xác định chức năng readObject () của riêng bạn để có thể đọc lại dữ liệu cũ. Mã tùy chỉnh này sau đó có thể kiểm tra serialVersionUIDđể biết dữ liệu nằm trong phiên bản nào và quyết định cách khử tuần tự hóa nó. Kỹ thuật tạo phiên bản này rất hữu ích nếu bạn lưu trữ dữ liệu tuần tự tồn tại một số phiên bản mã của bạn.

Nhưng lưu trữ dữ liệu nối tiếp trong một khoảng thời gian dài như vậy không phải là rất phổ biến. Việc sử dụng cơ chế tuần tự hóa để tạm thời ghi dữ liệu vào bộ đệm hoặc gửi nó qua mạng đến một chương trình khác có cùng phiên bản của các phần có liên quan của cơ sở dữ liệu.

Trong trường hợp này, bạn không quan tâm đến việc duy trì khả năng tương thích ngược. Bạn chỉ quan tâm đến việc đảm bảo rằng các cơ sở mã đang giao tiếp thực sự có cùng phiên bản của các lớp có liên quan. Để tạo điều kiện kiểm tra như vậy, bạn phải duy trì serialVersionUIDgiống như trước đây và không quên cập nhật nó khi thay đổi các lớp học của bạn.

Nếu bạn quên cập nhật trường, bạn có thể kết thúc với hai phiên bản khác nhau của một lớp có cấu trúc khác nhau nhưng có cùng cấu trúc serialVersionUID. Nếu điều này xảy ra, cơ chế mặc định ( in.defaultReadObject()) sẽ không phát hiện bất kỳ sự khác biệt nào và cố gắng hủy tuần tự hóa dữ liệu không tương thích. Bây giờ bạn có thể kết thúc với một lỗi thời gian chạy khó hiểu hoặc lỗi im lặng (các trường null). Những loại lỗi này có thể khó tìm.

Vì vậy, để giúp usecase này, nền tảng Java cung cấp cho bạn một lựa chọn không cài đặt serialVersionUIDthủ công. Thay vào đó, một hàm băm của cấu trúc lớp sẽ được tạo tại thời gian biên dịch và được sử dụng làm id. Cơ chế này sẽ đảm bảo rằng bạn không bao giờ có các cấu trúc lớp khác nhau với cùng một id và do đó bạn sẽ không gặp phải các lỗi tuần tự hóa thời gian chạy khó theo dõi được đề cập ở trên.

Nhưng có một mặt trái của chiến lược id được tạo tự động. Cụ thể là các id được tạo cho cùng một lớp có thể khác nhau giữa các trình biên dịch (như đã đề cập bởi Jon Skeet ở trên). Vì vậy, nếu bạn giao tiếp dữ liệu tuần tự giữa các mã được biên dịch với các trình biên dịch khác nhau, bạn nên duy trì id theo cách thủ công.

Và nếu bạn tương thích ngược với dữ liệu của bạn như trong trường hợp sử dụng đầu tiên được đề cập, có lẽ bạn cũng muốn tự duy trì id. Điều này để có được id có thể đọc và kiểm soát tốt hơn khi nào và cách chúng thay đổi.


4
Việc thêm hoặc loại bỏ các trường không nhất thời không làm cho việc tuần tự hóa lớp không tương thích. Do đó, không có lý do gì để 'đụng hàng' với những thay đổi như vậy.
Hầu tước Lorne

4
@EJP: Hả? Thêm dữ liệu chắc chắn thay đổi dữ liệu tuần tự hóa trong thế giới của tôi.
Alexander Torstling

3
@AlexanderTorstling Đọc những gì tôi đã viết. Tôi không nói rằng nó không 'thay đổi dữ liệu tuần tự hóa'. Tôi nói nó 'không làm cho tuần tự hóa lớp không tương thích'. Đó không phải là điều tương tự. Bạn cần đọc chương Phiên bản của Đặc tả tuần tự hóa đối tượng.
Hầu tước Lorne

3
@EJP: Tôi nhận ra rằng việc thêm một trường không tạm thời không nhất thiết có nghĩa là bạn làm cho tuần tự lớp không tương thích, nhưng đó là một thay đổi về cấu trúc làm thay đổi dữ liệu tuần tự và bạn thường muốn làm hỏng phiên bản khi làm như vậy trừ khi bạn xử lý tương thích ngược, mà tôi cũng đã giải thích sau trong bài viết. Quan điểm của bạn chính xác là gì?
Alexander Torstling

4
Quan điểm của tôi vẫn chính xác những gì tôi nói. Việc thêm hoặc xóa các trường không tạm thời không làm cho lớp Nối tiếp không tương thích. Do đó, bạn không cần phải nối tiếp serialVersionUID mỗi khi bạn làm như vậy.
Hầu tước Lorne

72

SerialVersionUID là gì và tại sao tôi nên sử dụng nó?

SerialVersionUIDlà một mã định danh duy nhất cho mỗi lớp, JVMsử dụng nó để so sánh các phiên bản của lớp đảm bảo rằng cùng một lớp được sử dụng trong quá trình Tuần tự hóa được tải trong quá trình Deserialization.

Việc chỉ định một cái cho nhiều quyền kiểm soát hơn, mặc dù JVM sẽ tạo một cái nếu bạn không chỉ định. Giá trị được tạo ra có thể khác nhau giữa các trình biên dịch khác nhau. Hơn nữa, đôi khi bạn chỉ muốn một số lý do để cấm khử lưu trữ các đối tượng nối tiếp cũ [ backward incompatibility], và trong trường hợp này bạn chỉ cần thay đổi serialVersionUID.

Các javadocs choSerializable biết :

tính toán serialVersionUID mặc định rất nhạy cảm với các chi tiết lớp có thể thay đổi tùy theo việc triển khai trình biên dịch và do đó có thể dẫn đến InvalidClassExceptions bất ngờ trong quá trình khử lưu huỳnh.

Do đó, bạn phải khai báo serialVersionUID vì nó cho chúng ta nhiều quyền kiểm soát hơn .

Bài viết này có một số điểm tốt về chủ đề này.


3
@Vinothbabu nhưng serialVersionUID là tĩnh nên các biến tĩnh không thể được tuần tự hóa. sau đó làm thế nào jvm sẽ kiểm tra phiên bản, mà không biết phiên bản của đối tượng
khử lưu huỳnh là gì

3
Một điều không được đề cập trong câu trả lời này là bạn có thể gây ra hậu quả ngoài ý muốn bằng cách mù quáng bao gồm serialVersionUIDmà không biết tại sao. Nhận xét của Tom Anderson về câu trả lời của MetroidFan2002 giải quyết vấn đề này: "Tôi muốn nói rằng nếu bạn không sử dụng tuần tự hóa để lưu trữ vĩnh viễn, bạn nên sử dụng @SuppressWarnings thay vì thêm một giá trị. Cơ chế serialVersionUID để bảo vệ bạn khỏi những thay đổi không tương thích. "
Kirby

7
serialVersionUIDkhông một 'định danh duy nhất cho mỗi lớp. Tên lớp đủ điều kiện là vậy. Đây là một chỉ số phiên bản .
Hầu tước Lorne

58

Câu hỏi ban đầu đã hỏi về 'tại sao nó quan trọng' và 'ví dụ' nơi điều này Serial Version IDsẽ hữu ích. Vâng, tôi đã tìm thấy một.

Giả sử bạn tạo một Carlớp, khởi tạo nó và viết nó ra một luồng đối tượng. Các đối tượng xe phẳng được đặt trong hệ thống tập tin một thời gian. Trong khi đó, nếu Carlớp được sửa đổi bằng cách thêm một trường mới. Sau này, khi bạn cố gắng đọc (tức là giải trừ) Carđối tượng dẹt , bạn sẽ nhận đượcjava.io.InvalidClassException tuần tự hóa - bởi vì tất cả các lớp tuần tự hóa được tự động đưa ra một định danh duy nhất. Ngoại lệ này được ném khi mã định danh của lớp không bằng mã định danh của đối tượng dẹt. Nếu bạn thực sự nghĩ về nó, ngoại lệ được ném vì bổ sung lĩnh vực mới. Bạn có thể tránh ngoại lệ này bị ném bằng cách tự kiểm soát phiên bản bằng cách khai báo serialVersionUID rõ ràng. Ngoài ra còn có một lợi ích hiệu suất nhỏ trong việc tuyên bố rõ ràng của bạnserialVersionUID(vì không phải tính toán). Vì vậy, cách tốt nhất là thêm serialVersionUID của riêng bạn vào các lớp Nối tiếp của bạn ngay khi bạn tạo chúng như dưới đây:

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}

Và người ta nên gán một số dài ngẫu nhiên, không phải 1L, cho mỗi UID.
abbas

4
@abbas 'Một người nên' làm điều đó tại sao? Hãy giải thích sự khác biệt của nó.
Hầu tước Lorne

Như tên đã nói, nó đại diện cho phiên bản của lớp được sử dụng để tuần tự hóa đối tượng. Nếu bạn gán cùng một số mỗi lần, bạn sẽ không thể tìm đúng lớp để hủy tuần tự hóa nó. Vì vậy, bất cứ khi nào bạn thực hiện một thay đổi trong lớp, tốt hơn là thay đổi phiên bản quá.
abbas

2
@abbas, ý định này không xung đột với việc sử dụng số tự nhiên tăng dần từ 1vân vân.
Vadzim

2
@BillK, tôi nghĩ rằng kiểm tra tuần tự hóa được ràng buộc với cặp tên lớp và serialVersionUID. Vì vậy, các sơ đồ đánh số khác nhau của các lớp và thư viện khác nhau không thể can thiệp vào bất kỳ cách nào. Hay bạn đã ám chỉ các thư viện tạo mã?
Vadzim

44

Đầu tiên tôi cần giải thích serialization là gì.

Tuần tự hóa cho phép chuyển đổi một đối tượng thành một luồng, để gửi đối tượng đó qua mạng HOẶC Lưu vào tệp HOẶC lưu vào DB để sử dụng chữ cái.

Có một số quy tắc cho tuần tự hóa .

  • Một đối tượng chỉ được tuần tự hóa nếu lớp hoặc siêu lớp của nó thực hiện giao diện Nối tiếp

  • Một đối tượng được tuần tự hóa (chính nó thực hiện giao diện Nối tiếp) ngay cả khi siêu lớp của nó không. Tuy nhiên, siêu lớp đầu tiên trong hệ thống phân cấp của lớp tuần tự hóa, không thực hiện giao diện Nối tiếp, PHẢI có một hàm tạo không có đối số. Nếu điều này bị vi phạm, readObject () sẽ tạo java.io.InvalidClassException trong thời gian chạy

  • Tất cả các loại nguyên thủy là tuần tự.

  • Các trường tạm thời (với công cụ sửa đổi nhất thời) KHÔNG được tuần tự hóa, (nghĩa là không được lưu hoặc khôi phục). Một lớp thực hiện tuần tự hóa phải đánh dấu các trường tạm thời của các lớp không hỗ trợ tuần tự hóa (ví dụ: một luồng tệp).

  • Các trường tĩnh (với công cụ sửa đổi tĩnh) không được tuần tự hóa.

Khi Objectđược tuần tự hóa, Java Runtime liên kết số phiên bản nối tiếp aka , serialVersionID.

Nơi chúng tôi cần serialVersionID: Trong quá trình khử lưu huỳnh để xác minh rằng người gửi và người nhận có tương thích với việc tuần tự hóa không. Nếu người nhận nạp lớp với một khácserialVersionIDthì việc khử lưu huỳnh sẽ kết thúc bằngInvalidClassCastException.
Một lớp tuần tự hóa có thể tự khai báo mộtserialVersionUIDcách rõ ràng bằng cách khai báo một trường có tênserialVersionUIDphải là tĩnh, cuối cùng và có kiểu dài.

Hãy thử làm điều này với một ví dụ.

import java.io.Serializable;    
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;

public String getEmpName() {
    return name;
}
public void setEmpName(String empname) {
    this.empname = empname;
}
public byte getEmpAge() {
    return empage;
}
public void setEmpAge(byte empage) {
    this.empage = empage;
}

public String whoIsThis() {
    StringBuffer employee = new StringBuffer();
    employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old  "));
    return employee.toString();
}
}

Tạo đối tượng nối tiếp

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
    Employee employee = new Employee();
    employee.setEmpName("Jagdish");
    employee.setEmpAge((byte) 30);

    FileOutputStream fout = new 
FileOutputStream("/users/Jagdish.vala/employee.obj");
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(employee);
    oos.close();
    System.out.println("Process complete");
}
}

Giải trừ đối tượng

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, 
IOException {
    Employee employee = new Employee();
    FileInputStream fin = new 
    FileInputStream("/users/Jagdish.vala/employee.obj");
    ObjectInputStream ois = new ObjectInputStream(fin);
    employee = (Employee) ois.readObject();
    ois.close();
    System.out.println(employee.whoIsThis());
 }
}    

LƯU Ý: Bây giờ thay đổi serialVersionUID của lớp Nhân viên và lưu:

private static final long serialVersionUID = 4L;

Và thực hiện lớp Reader. Không thực thi lớp Writer và bạn sẽ có ngoại lệ.

Exception in thread "main" java.io.InvalidClassException: 
com.jagdish.vala.java.serialVersion.Employee; local class incompatible: 
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)

Sửa lỗi cho tôi nếu tôi sai - lớp cục bộ là lớp mà bạn hiện đang có / đang sử dụng trong đường dẫn lớp và luồng là lớp được sử dụng bởi một bên khác (ví dụ: máy chủ trả về cho bạn câu trả lời và đã tuần tự hóa phản ứng). Bạn có thể gặp tình huống này khi bạn đang liên lạc với một máy chủ đã cập nhật lib của bên thứ 3, nhưng bạn (khách hàng) đã không làm điều này.
Victor

37

Nếu bạn sẽ không bao giờ cần phải tuần tự hóa các đối tượng của mình thành mảng byte và gửi / lưu trữ chúng, thì bạn không cần phải lo lắng về điều đó. Nếu bạn làm như vậy, thì bạn phải xem xét serialVersionUID của mình vì trình giải nén của đối tượng sẽ khớp nó với phiên bản của đối tượng mà trình nạp lớp của nó có. Đọc thêm về nó trong Đặc tả ngôn ngữ Java.


10
Nếu bạn sẽ không tuần tự hóa các đối tượng, tại sao chúng lại được tuần tự hóa?
erickson

7
@erickson - lớp cha có thể được tuần tự hóa, giả sử ArrayList, nhưng bạn muốn đối tượng của riêng bạn (giả sử, một danh sách mảng đã sửa đổi) sử dụng nó làm cơ sở nhưng sẽ không bao giờ tuần tự hóa Bộ sưu tập mà bạn tạo.
MetroidFan2002

5
Nó không được đề cập ở bất cứ đâu trong Đặc tả ngôn ngữ Java. Nó được đề cập trong Đặc tả phiên bản đối tượng.
Hầu tước Lorne

4
Đây là liên kết đến Đặc tả phiên bản đối tượng Java 8 .
Basil Bourque

34

Nếu bạn nhận được cảnh báo này trên một lớp học, bạn không bao giờ nghĩ về việc tuần tự hóa và rằng bạn đã không tuyên bố chính mình implements Serializable , thì đó thường là do bạn được thừa hưởng từ một siêu lớp, trong đó thực hiện Nối tiếp. Thông thường sau đó sẽ tốt hơn khi ủy thác cho một đối tượng như vậy thay vì sử dụng kế thừa.

Vì vậy, thay vì

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

làm

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

và trong các phương thức liên quan gọi myList.foo()thay vì this.foo()(hoặc super.foo()). (Điều này không phù hợp trong mọi trường hợp, nhưng vẫn khá thường xuyên.)

Tôi thường thấy mọi người mở rộng JFrame hoặc như vậy, khi họ thực sự chỉ cần ủy thác cho việc này. (Điều này cũng giúp tự động hoàn thành trong IDE, vì JFrame có hàng trăm phương thức mà bạn không cần khi bạn muốn gọi các tùy chỉnh của mình trên lớp.)

Một trường hợp không thể tránh khỏi cảnh báo (hoặc serialVersionUID) là khi bạn mở rộng từ AbstractAction, thông thường trong một lớp ẩn danh, chỉ thêm phương thức actionPerformed. Tôi nghĩ rằng không nên có cảnh báo trong trường hợp này (vì thông thường bạn không thể tin cậy tuần tự hóa và giải tuần tự hóa các lớp ẩn danh như vậy qua các phiên bản khác nhau của lớp bạn), nhưng tôi không chắc làm thế nào trình biên dịch có thể nhận ra điều này.


4
Tôi nghĩ rằng bạn đúng rằng sáng tác trên tính không hợp lý có ý nghĩa hơn, đặc biệt là khi bạn đang thảo luận về các lớp như ArrayList. Tuy nhiên, nhiều khung công tác yêu cầu mọi người mở rộng từ các siêu lớp trừu tượng có khả năng tuần tự hóa (chẳng hạn như lớp ActionForm của Struts 1.2, hoặc ExtensionFunctionDefDef của Saxon) trong trường hợp giải pháp này là không khả thi. Tôi nghĩ bạn đã đúng, sẽ rất tuyệt nếu cảnh báo bị bỏ qua trong một số trường hợp nhất định (như nếu bạn đang mở rộng từ một lớp tuần tự trừu tượng)
piepera

2
Chắc chắn nếu bạn thêm một lớp làm thành viên, thay vì kế thừa từ nó, bạn sẽ phải viết một phương thức trình bao bọc cho MỌI phương thức của lớp thành viên mà bạn muốn sử dụng, điều này sẽ không khả thi trong một số lượng lớn các tình huống .. trừ khi java có chức năng tương tự như perl __AUTOLOAD, mà tôi không biết.
M_M

4
@M_M: Khi bạn sẽ ủy thác nhiều phương thức cho đối tượng được bao bọc của mình, tất nhiên sẽ không phù hợp khi sử dụng ủy quyền. Nhưng tôi cho rằng trường hợp này là dấu hiệu của lỗi thiết kế - người dùng của lớp của bạn (ví dụ: "MainGui") không cần phải gọi nhiều phương thức của đối tượng được bọc (ví dụ JFrame).
Paŭlo Ebermann

1
Điều tôi không thích về phái đoàn là cần phải có một tài liệu tham khảo cho đại biểu. Và mỗi tham chiếu có nghĩa là nhiều bộ nhớ hơn. Sửa tôi nếu tôi sai. Nếu tôi cần một CustomArrayList gồm 100 đối tượng thì điều này sẽ không quan trọng lắm nhưng nếu tôi cần hàng trăm CustomizdeArrayLists của một vài đối tượng thì việc sử dụng bộ nhớ tăng lên đáng kể.
WVrock

2
Có thể ném được là tuần tự hóa và chỉ có thể ném được là có thể ném được, do đó không thể xác định một ngoại lệ không thể tuần tự hóa. Đoàn không thể.
Phil

22

Để hiểu tầm quan trọng của serialVersionUID trường, người ta nên hiểu cách serialization / Deserialization hoạt động.

Khi một đối tượng lớp Nối tiếp được tuần tự hóa, Java Runtime kết hợp một phiên bản nối tiếp số (được gọi là serialVersionUID) với đối tượng được tuần tự hóa này. Tại thời điểm bạn giải tuần tự hóa đối tượng được tuần tự hóa này, Java Runtime khớp với serialVersionUID của đối tượng được tuần tự hóa với serialVersionUID của lớp. Nếu cả hai đều bằng nhau thì chỉ có nó mới tiến hành quá trình khử lưu huỳnh khác, ném ra UnlimitedClassException.

Vì vậy, chúng tôi kết luận rằng để thực hiện quá trình Tuần tự hóa / Giải tuần tự thành công, serialVersionUID của đối tượng được tuần tự hóa phải tương đương với serialVersionUID của lớp. Trong trường hợp nếu lập trình viên chỉ định rõ ràng giá trị serialVersionUID trong chương trình thì cùng một giá trị sẽ được liên kết với đối tượng được tuần tự hóa và lớp, bất kể nền tảng tuần tự hóa và giải tuần tự hóa (ví dụ như tuần tự hóa có thể được thực hiện trên nền tảng như windows hoặc bằng cách sử dụng sun hoặc MS JVM và Deserialization có thể trên Linux nền tảng khác nhau bằng cách sử dụng Zing JVM).

Nhưng trong trường hợp nếu serialVersionUID không được chỉ định bởi lập trình viên thì trong khi thực hiện serialization \ DeSerialization của bất kỳ đối tượng nào, thời gian chạy Java sử dụng thuật toán riêng của nó để tính toán nó. Thuật toán tính toán serialVersionUID này thay đổi từ JRE này sang JRE khác. Cũng có thể môi trường mà đối tượng được tuần tự hóa đang sử dụng một JRE (ví dụ: SUN JVM) và môi trường xảy ra quá trình khử lưu huỳnh đang sử dụng Linux Jvm (zing). Trong các trường hợp như vậy, serialVersionUID được liên kết với đối tượng được tuần tự hóa sẽ khác với serialVersionUID của lớp được tính toán trong môi trường khử lưu huỳnh. Lần lượt khử lưu huỳnh sẽ không thành công. Vì vậy, để tránh các tình huống / vấn đề như vậy, lập trình viên phải luôn chỉ định serialVersionUID của lớp Nối tiếp.


2
Thuật toán không thay đổi, nhưng nó hơi bị chỉ định.
Hầu tước Lorne

... thuật toán không thay đổi, nhưng nó là hơi dưới định ... nghĩa là bất kỳ JVM có thể thay đổi ..... @ user207421
AnthonyJClink

18

Đừng bận tâm, tính toán mặc định thực sự tốt và đủ cho 99,9999% các trường hợp. Và nếu bạn gặp vấn đề, bạn có thể - như đã nêu - giới thiệu UID khi có nhu cầu (điều này rất khó xảy ra)


2
Rác. Nó là đủ trong trường hợp lớp không thay đổi. Bạn không có bằng chứng để hỗ trợ '99 .9999% '.
Hầu tước Lorne

18

Đối với một ví dụ trong đó serialVersionUID bị thiếu có thể gây ra sự cố:

Tôi đang làm việc trên ứng dụng Java EE này bao gồm một mô-đun Web sử dụng một EJBmô-đun. Mô-đun web gọi EJBmô-đun từ xa và chuyển một POJOthực hiện Serializablenhư là một đối số.

Đây POJO'slớp được đóng gói bên trong jar EJB và bên trong lọ riêng của nó trong thư mục WEB-INF / lib của module web. Chúng thực sự là cùng một lớp, nhưng khi tôi đóng gói mô-đun EJB, tôi mở gói jar của POJO này để đóng gói cùng với mô-đun EJB.

Cuộc gọi đến EJBđã thất bại với Ngoại lệ bên dưới vì tôi đã không khai báo serialVersionUID:

Caused by: java.io.IOException: Mismatched serialization UIDs : Source
 (Rep.
 IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
 = 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
 = 6227F23FA74A9A52

16

Tôi thường sử dụng serialVersionUIDtrong một ngữ cảnh: Khi tôi biết nó sẽ rời khỏi bối cảnh của máy ảo Java.

Tôi sẽ biết điều này khi tôi sử dụng ObjectInputStreamObjectOutputStreamcho ứng dụng của tôi hoặc nếu tôi biết một thư viện / khung tôi sử dụng sẽ sử dụng nó. SerialVersionID đảm bảo các máy ảo Java khác nhau của các phiên bản hoặc nhà cung cấp khác nhau sẽ hoạt động chính xác hoặc nếu nó được lưu trữ và truy xuất bên ngoài VM, ví dụ HttpSession, dữ liệu phiên có thể vẫn còn trong khi khởi động lại và nâng cấp máy chủ ứng dụng.

Đối với tất cả các trường hợp khác, tôi sử dụng

@SuppressWarnings("serial")

vì hầu hết thời gian mặc định serialVersionUIDlà đủ. Điều này bao gồm Exception, HttpServlet.


Nó không bao gồm httpServlet trong các thùng chứa nơi chúng có thể được hoán đổi hoặc Ngoại lệ trong RMI chẳng hạn.
Hầu tước Lorne

14

Dữ liệu trường đại diện cho một số thông tin được lưu trữ trong lớp. Lớp thực hiện Serializablegiao diện, do đó nhật thực tự động được cung cấp để khai báo serialVersionUIDtrường. Hãy bắt đầu với giá trị 1 được đặt ở đó.

Nếu bạn không muốn cảnh báo đó đến, hãy sử dụng:

@SuppressWarnings("serial")

11

Sẽ thật tuyệt nếu CheckStyle có thể xác minh rằng serialVersionUID trên một lớp thực hiện serializable có giá trị tốt, tức là nó phù hợp với những gì trình tạo id phiên bản nối tiếp sẽ tạo ra. Ví dụ, nếu bạn có một dự án có nhiều DTO tuần tự hóa, hãy nhớ xóa serialVersionUID hiện có và tạo lại nó là một nỗi đau, và hiện tại là cách duy nhất (mà tôi biết) để xác minh điều này là tái tạo cho mỗi lớp và so sánh với cái cũ. Điều này rất rất đau đớn.


8
Nếu bạn luôn đặt serialVersionUID thành cùng một giá trị mà trình tạo sẽ tạo ra, bạn hoàn toàn không cần nó. Rốt cuộc, lý lịch của nó là giữ nguyên sau khi thay đổi, khi lớp vẫn tương thích.
Paŭlo Ebermann

4
Lý do là vì các trình biên dịch khác nhau đưa ra cùng một giá trị cho cùng một lớp. Như đã giải thích trong javadocs (cũng đã trả lời ở trên), phiên bản được tạo ra dễ vỡ và có thể thay đổi ngay cả khi lớp được khử lưu lượng chính xác. Miễn là bạn chạy thử nghiệm này trên cùng một trình biên dịch mỗi lần, nó sẽ an toàn. Chúa sẽ giúp bạn nếu bạn nâng cấp jdk và một quy tắc mới được đưa ra, mặc dù mã của bạn không thay đổi.
Andrew Backer

2
Nó không bắt buộc phải phù hợp với những gì serialversẽ sản xuất. -1
Hầu tước Lorne

Nói chung, nó không bắt buộc. Trường hợp của @ AndrewBacker's sẽ yêu cầu hai trình biên dịch khác nhau trên cùng một tệp .java với cả hai phiên bản của các tệp. Class giao tiếp với nhau - hầu hết thời gian bạn chỉ cần tạo lớp và phân phối nó. Nếu đó là trường hợp, thì không có SUID sẽ hoạt động tốt.
Bill K

1
Những người thực sự sử dụng Tuần tự hóa cho mục đích lưu trữ / truy xuất thường sẽ đặt thành serialVersionUID1. Nếu một phiên bản mới hơn của lớp không tương thích, nhưng vẫn yêu cầu có thể xử lý dữ liệu cũ, bạn tăng số phiên bản và thêm mã đặc biệt vào đối phó với các định dạng cũ hơn. Tôi khóc mỗi khi tôi thấy một serialVersionUIDchữ số lớn hơn 1 chữ số, vì đó là một số ngẫu nhiên (vô dụng) hoặc vì lớp rõ ràng cần phải xử lý hơn 10 phiên bản khác nhau.
john16384

11

SerialVersionUID được sử dụng để kiểm soát phiên bản của đối tượng. bạn cũng có thể chỉ định serialVersionUID trong tệp lớp của mình. Hậu quả của việc không chỉ định serialVersionUID là khi bạn thêm hoặc sửa đổi bất kỳ trường nào trong lớp thì lớp đã được tuần tự hóa sẽ không thể phục hồi vì serialVersionUID được tạo cho lớp mới và đối tượng được tuần tự hóa cũ sẽ khác nhau. Quá trình tuần tự hóa Java dựa trên serialVersionUID chính xác để khôi phục trạng thái của đối tượng được tuần tự hóa và ném java.io.InvalidClassException trong trường hợp không khớp serialVersionUID

Đọc thêm: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ


9

Tại sao sử dụng SerialVersionUIDbên trong Serializablelớp trong Java?

Trong serializationthời gian chạy, Java tạo một số phiên bản cho một lớp để nó có thể hủy tuần tự hóa nó sau này. Số phiên bản này được gọi là SerialVersionUIDtrong Java.

SerialVersionUIDđược sử dụng để phiên bản dữ liệu nối tiếp. Bạn chỉ có thể hủy tuần tự hóa một lớp nếu nó SerialVersionUIDphù hợp với thể hiện được tuần tự hóa. Khi chúng tôi không khai báo SerialVersionUIDtrong lớp, thời gian chạy Java sẽ tạo nó cho chúng tôi nhưng nó không được khuyến nghị. Bạn nên khai báo SerialVersionUIDprivate static final longbiến để tránh cơ chế mặc định.

Khi bạn khai báo một lớp Serializablebằng cách triển khai giao diện đánh dấu java.io.Serializable, thời gian chạy Java vẫn tồn tại thể hiện của lớp đó vào đĩa bằng cách sử dụng cơ chế Tuần tự hóa mặc định, miễn là bạn không tùy chỉnh quy trình bằng Externalizablegiao diện.

xem thêm Tại sao nên sử dụng SerialVersionUID bên trong lớp Nối tiếp trong Java


8

Nếu bạn muốn sửa đổi một số lượng lớn các lớp không có serialVersionUID được đặt ở vị trí đầu tiên trong khi vẫn duy trì khả năng tương thích với các lớp cũ, các công cụ như IntelliJ Idea, Eclipse sẽ bị thiếu khi chúng tạo ra các số ngẫu nhiên và không hoạt động trên một loạt các tệp chỉ trong một bước. Tôi đưa ra tập lệnh bash sau (tôi xin lỗi cho người dùng Windows, xem xét việc mua máy Mac hoặc chuyển đổi sang Linux) để sửa đổi vấn đề serialVersionUID một cách dễ dàng:

base_dir=$(pwd)                                                                  
src_dir=$base_dir/src/main/java                                                  
ic_api_cp=$base_dir/target/classes                                               

while read f                                                                     
do                                                                               
    clazz=${f//\//.}                                                             
    clazz=${clazz/%.java/}                                                       
    seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
    perl -ni.bak -e "print $_; printf qq{%s\n}, q{    private $seruidstr} if /public class/" $src_dir/$f
done

bạn lưu tập lệnh này, giả sử add_serialVersionUID.sh cho bạn ~ / bin. Sau đó, bạn chạy nó trong thư mục gốc của dự án Maven hoặc Gradle của bạn như:

add_serialVersionUID.sh < myJavaToAmend.lst

.Lst này bao gồm danh sách các tệp java để thêm serialVersionUID theo định dạng sau:

com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java

Kịch bản lệnh này sử dụng công cụ serialVer JDK dưới mui xe. Vì vậy, hãy đảm bảo $ JAVA_HOME / bin của bạn nằm trong PATH.


2
Cung cấp cho tôi một ý tưởng: luôn luôn tạo lại phiên bản nối tiếp uid bằng một công cụ như thế này, không bao giờ bằng tay, trước khi phát hành - theo cách đó, bạn tránh quên thay đổi một lớp có phiên bản nối tiếp uid nên thay đổi vì thực tế thay đổi không tương thích. Theo dõi điều đó bằng tay là rất khó.
Ignazio

6

Câu hỏi này được tài liệu rất tốt trong Java hiệu quả của Joshua Bloch. Một cuốn sách rất tốt và phải đọc. Tôi sẽ phác thảo một số lý do dưới đây:

Thời gian chạy tuần tự đi kèm với một số gọi là phiên bản nối tiếp cho mỗi lớp tuần tự hóa. Số này được gọi là serialVersionUID. Bây giờ có một số Toán học đằng sau con số này và nó xuất hiện dựa trên các trường / phương thức được xác định trong lớp. Đối với cùng một lớp, phiên bản tương tự được tạo ra mỗi lần. Số này được sử dụng trong quá trình khử lưu huỳnh để xác minh rằng người gửi và người nhận của một đối tượng được tuần tự hóa đã tải các lớp cho đối tượng đó tương thích với việc tuần tự hóa. Nếu người nhận đã tải một lớp cho đối tượng có serialVersionUID khác với lớp của người gửi tương ứng, thì quá trình khử lưu huỳnh sẽ dẫn đến một UnlimitedClassException.

Nếu lớp được tuần tự hóa, bạn cũng có thể khai báo serialVersionUID của riêng mình một cách rõ ràng bằng cách khai báo một trường có tên "serialVersionUID" phải là tĩnh, cuối cùng và có kiểu dài. Hầu hết các IDE như Eclipse giúp bạn tạo ra chuỗi dài đó.


6

Mỗi khi một đối tượng được tuần tự hóa, đối tượng được đóng dấu số ID phiên bản cho lớp của đối tượng. ID này được gọi là serialVersionUID và nó được tính dựa trên thông tin về cấu trúc lớp. Giả sử bạn đã tạo một lớp Nhân viên và nó có phiên bản id # 333 (được gán bởi JVM), Bây giờ khi bạn sẽ tuần tự hóa đối tượng của lớp đó (Giả sử đối tượng Nhân viên), JVM sẽ gán UID cho nó là # 333.

Xem xét một tình huống - trong tương lai bạn cần chỉnh sửa hoặc thay đổi lớp của mình và trong trường hợp đó khi bạn sửa đổi nó, JVM sẽ gán cho nó một UID mới (Giả sử # 444). Bây giờ khi bạn cố gắng giải tuần tự hóa đối tượng nhân viên, JVM sẽ so sánh ID phiên bản (đối tượng nhân viên) được tuần tự hóa (# 333) với đối tượng của lớp tức là # 444 (Vì nó đã được thay đổi). Khi so sánh, JVM sẽ tìm thấy cả hai phiên bản UID khác nhau và do đó Deserialization sẽ thất bại. Do đó, nếu serialVersionID cho mỗi lớp được xác định bởi chính lập trình viên. Nó sẽ giống nhau ngay cả khi lớp được phát triển trong tương lai và do đó JVM sẽ luôn thấy lớp đó tương thích với đối tượng được tuần tự hóa ngay cả khi lớp bị thay đổi. Để biết thêm thông tin, bạn có thể tham khảo chương 14 của JAVA ĐẦU TIÊN.


1
Mỗi khi lớp của một đối tượng được tuần tự hóa, serialVersionUIDnó được truyền đi. Nó không được gửi với mọi đối tượng.
Hầu tước Lorne

3

Một lời giải thích đơn giản:

  1. Bạn đang tuần tự hóa dữ liệu?

    Tuần tự hóa về cơ bản là ghi dữ liệu lớp vào một tệp / luồng / vv. Khử tuần tự hóa đang đọc dữ liệu đó trở lại một lớp.

  2. Bạn có ý định đi vào sản xuất?

    Nếu bạn chỉ đang thử nghiệm một cái gì đó với dữ liệu không quan trọng / giả mạo, thì đừng lo lắng về điều đó (trừ khi bạn đang thử nghiệm tuần tự hóa trực tiếp).

  3. Đây có phải là phiên bản đầu tiên?

    Nếu vậy, thiết lập serialVersionUID=1L.

  4. Đây có phải là phiên bản prod thứ hai, thứ ba, vv?

    Bây giờ bạn cần phải lo lắng serialVersionUID, và nên nhìn sâu vào nó.

Về cơ bản, nếu bạn không cập nhật phiên bản chính xác khi bạn cập nhật một lớp bạn cần viết / đọc, bạn sẽ gặp lỗi khi bạn cố đọc dữ liệu cũ.


2

'serialVersionUID' là số 64 bit được sử dụng để xác định duy nhất một lớp trong quá trình khử lưu huỳnh. Khi bạn tuần tự hóa một đối tượng, serialVersionUID của lớp cũng được ghi vào tệp. Bất cứ khi nào bạn giải tuần tự hóa đối tượng này, java chạy thời gian trích xuất giá trị serialVersionUID này từ dữ liệu được tuần tự hóa và so sánh cùng giá trị liên kết với lớp. Nếu cả hai không khớp nhau, thì 'java.io.InvalidClassException' sẽ bị ném.

Nếu một lớp tuần tự hóa không khai báo rõ ràng một serialVersionUID, thì thời gian chạy tuần tự hóa sẽ tính toán giá trị serialVersionUID cho lớp đó dựa trên các khía cạnh khác nhau của lớp như các trường, các phương thức, v.v. Bạn có thể tham khảo liên kết này cho ứng dụng demo.


1

Để kể câu chuyện dài, trường này được sử dụng để kiểm tra xem dữ liệu nối tiếp có thể được giải tuần tự hóa một cách chính xác hay không. Tuần tự hóa và giải tuần tự hóa thường được thực hiện bởi các bản sao khác nhau của chương trình - ví dụ: máy chủ chuyển đổi đối tượng thành chuỗi và máy khách chuyển đổi chuỗi nhận được thành đối tượng. Trường này cho biết cả hai hoạt động với cùng một ý tưởng về đối tượng này là gì. Trường này giúp khi:

  • bạn có nhiều bản sao chương trình khác nhau ở những nơi khác nhau (như 1 máy chủ và 100 máy khách). Nếu bạn sẽ thay đổi đối tượng của mình, thay đổi số phiên bản của bạn và quên cập nhật một ứng dụng khách này, nó sẽ biết rằng anh ta không có khả năng khử lưu huỳnh

  • bạn đã lưu trữ dữ liệu của mình trong một số tệp và sau đó bạn cố gắng mở nó với phiên bản cập nhật của chương trình với đối tượng đã sửa đổi - bạn sẽ biết rằng tệp này không tương thích nếu bạn giữ đúng phiên bản của mình

Khi nào nó quan trọng?

Rõ ràng nhất - nếu bạn thêm một số trường vào đối tượng của mình, các phiên bản cũ hơn sẽ không thể sử dụng chúng vì chúng không có các trường này trong cấu trúc đối tượng của chúng.

Ít rõ ràng hơn - Khi bạn khử lưu trữ đối tượng, các trường không có trong chuỗi sẽ được giữ dưới dạng NULL. Nếu bạn đã xóa trường khỏi đối tượng của mình, các phiên bản cũ hơn sẽ giữ trường này là allways-NULL có thể dẫn đến hành vi sai nếu các phiên bản cũ hơn dựa vào dữ liệu trong trường này (dù sao bạn đã tạo nó cho mục đích nào đó, không chỉ để giải trí :-))

Ít nhất là rõ ràng - Đôi khi bạn thay đổi ý tưởng bạn đặt trong ý nghĩa của một số lĩnh vực. Ví dụ: khi bạn 12 tuổi, bạn có nghĩa là "xe đạp" trong "xe đạp", nhưng khi bạn 18 tuổi, bạn có nghĩa là "xe máy" - nếu bạn bè của bạn sẽ mời bạn "đạp xe qua thành phố" và bạn sẽ là người duy nhất đã đến trên xe đạp, bạn sẽ tiết lộ tầm quan trọng của việc giữ cùng một ý nghĩa trên các lĩnh vực :-)


1

Trước tiên để trả lời câu hỏi của bạn, khi chúng tôi không khai báo SerialVersionUID trong lớp của chúng tôi, thời gian chạy Java tạo ra nó cho chúng tôi, nhưng quá trình đó nhạy cảm với nhiều dữ liệu meta lớp bao gồm số trường, loại trường, công cụ sửa đổi truy cập của trường, giao diện được triển khai theo lớp, do đó, chúng tôi khuyên bạn nên tự khai báo và Eclipse đang cảnh báo bạn về điều tương tự.

Tuần tự hóa: Chúng tôi thường làm việc với các đối tượng quan trọng có trạng thái (dữ liệu trong các biến của đối tượng) quan trọng đến mức chúng tôi không thể mạo hiểm để mất nó do lỗi nguồn / hệ thống (hoặc) lỗi mạng trong trường hợp gửi trạng thái đối tượng sang trạng thái khác máy móc. Giải pháp cho vấn đề này được đặt tên là "Kiên trì" có nghĩa đơn giản là duy trì (giữ / lưu) dữ liệu. Tuần tự hóa là một trong nhiều cách khác để đạt được sự bền bỉ (bằng cách lưu dữ liệu vào đĩa / bộ nhớ). Khi lưu trạng thái của đối tượng, điều quan trọng là tạo một danh tính cho đối tượng, để có thể đọc lại chính xác (khử tuần tự hóa). Nhận dạng duy nhất này là ID là serialVersionUID.


0

SerialVersionUID là gì? Trả lời: - Hãy nói rằng có hai người, một người từ HQ và một người khác từ ODC, cả hai sẽ thực hiện tuần tự hóa và giải tuần tự hóa tương ứng. Trong trường hợp này để xác thực rằng người nhận trong ODC là người được xác thực, JVM tạo một ID duy nhất được gọi là serialVersionUID.

Đây là một lời giải thích tốt đẹp dựa trên kịch bản,

Tại sao serialVersionUID?

Tuần tự hóa : Tại thời điểm tuần tự hóa, với mỗi JVM bên gửi đối tượng sẽ lưu một Mã định danh duy nhất. JVM chịu trách nhiệm tạo ID duy nhất đó dựa trên tệp. Class tương ứng có trong hệ thống người gửi.

Deserialization : Tại thời điểm khử lưu huỳnh, JVM bên nhận sẽ so sánh ID duy nhất được liên kết với Object với ID cục bộ lớp duy nhất tức là JVM cũng sẽ tạo một ID duy nhất dựa trên tệp. Class tương ứng có trong hệ thống máy thu. Nếu cả hai ID duy nhất khớp nhau thì chỉ thực hiện khử lưu huỳnh sẽ được thực hiện. Nếu không, chúng tôi sẽ nhận được Runtime Exception nói UnlimitedClassException. Mã định danh duy nhất này không có gì ngoài SerialVersionUID

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.