Tại sao tạo serialVersionUID dài thay vì 1L đơn giản?


210

Khi lớp thực hiện tuần tự hóa trong Eclipse, tôi có hai tùy chọn: thêm mặc định serialVersionUID(1L)hoặc được tạo serialVersionUID(3567653491060394677L). Tôi nghĩ rằng cái đầu tiên mát hơn, nhưng nhiều lần tôi thấy mọi người sử dụng tùy chọn thứ hai. Có bất kỳ lý do để tạo ra long serialVersionUID?


49
Làm thế nào là chính xác trùng lặp? Tôi không hỏi tại sao lại tạo ra nó, nhưng tại sao lại tạo serialVersionUID dài.
IAd CHƯƠNG

13
Khi Jon Skeet sử dụng serialVersionUID, anh ta sử dụng 0L: stackoverflow.com/questions/605828/ Kẻ ;)
Hanno Fietz

8
@HannoFietz: Câu chính xác là: "Để đơn giản, tôi khuyên bạn nên bắt đầu bằng 0 và tăng thêm 1 mỗi lần bạn cần." Vì vậy, có vẻ như anh ta 0Lchỉ sử dụng ban đầu.
HOẶC Mapper

7
@ORMapper: Bạn đang ám chỉ rằng Jon Skeet có bao giờ cần quay lại và cập nhật mã anh ấy đã viết không? Thậm chí đến mức không tương thích về cấu trúc. Thở hổn hển! Dị giáo!
Thilo

Câu trả lời:


90

Theo như tôi có thể nói, điều đó sẽ chỉ tương thích với các phiên bản trước. Điều này sẽ chỉ hữu ích nếu bạn bỏ qua việc sử dụng serialVersionUID trước đó và sau đó thực hiện một thay đổi mà bạn biết phải tương thích nhưng điều đó làm cho việc xê-ri hóa bị phá vỡ.

Xem Thông số tuần tự hóa Java để biết thêm chi tiết.


69

Mục đích của phiên bản tuần tự hóa UID là theo dõi các phiên bản khác nhau của một lớp để thực hiện tuần tự hóa các đối tượng hợp lệ.

Ý tưởng là tạo một ID duy nhất cho một phiên bản nhất định của một lớp, sau đó được thay đổi khi có các chi tiết mới được thêm vào lớp, chẳng hạn như một trường mới, sẽ ảnh hưởng đến cấu trúc của đối tượng được tuần tự hóa.

Luôn luôn sử dụng cùng một ID, chẳng hạn như 1Ltrong tương lai, nếu định nghĩa lớp bị thay đổi gây ra thay đổi cấu trúc của đối tượng được tuần tự hóa, sẽ có nhiều khả năng xảy ra sự cố khi cố gắng khử tuần tự hóa một đối tượng.

Nếu ID bị bỏ qua, Java thực sự sẽ tính toán ID cho bạn dựa trên các trường của đối tượng, nhưng tôi tin rằng đó là một quá trình tốn kém, vì vậy việc cung cấp một cách thủ công sẽ cải thiện hiệu suất.

Dưới đây là một vài liên kết đến các bài viết thảo luận về tuần tự hóa và phiên bản các lớp:


50
Ý tưởng đằng sau việc sử dụng 1L là để bạn tăng nó mỗi khi bạn thay đổi các thuộc tính hoặc phương thức của lớp.
Powerlord

39
Không có tác động hiệu năng thời gian chạy khi cho phép serialversionUID được tạo tự động - nó được tạo tại thời gian biên dịch bởi javac ... nếu bạn dịch ngược mã byte của lớp, bạn thực sự sẽ thấy biến tĩnh trong mã byte.
Jared

11
Thêm một lưu ý - bằng cách quản lý số một cách rõ ràng, bạn có thể quyết định khi bạn xem xét các phiên bản của một lớp "tương thích", thay vì yêu cầu định nghĩa lớp phải giống hệt nhau.
Scott Stanchfield

23
@ Jared Theo Mục 75 trong Java hiệu quả của Josh Bloch: Phiên bản thứ 2: "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 .... Nếu không có phiên bản nối tiếp UID 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 . "
Colin K

12
@coobird Đây dường như là lý do chính tại sao serialVersionUID mặc định không được khuyến nghị 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. Nhận xét trên được lấy từ Đặc tả kỹ thuật tuần tự hóa đối tượng Java phiên bản 6.0
user624558

20

Lý do chính cho cái được tạo sẽ là làm cho nó tương thích với một phiên bản hiện có của lớp đã tồn tại các bản sao.


1
OK nhưng tương tự sẽ là Nếu tôi luôn có 1L. Mọi thứ sẽ tương thích ngay cả khi tôi sẽ thực hiện bất kỳ thay đổi.
grep

@grep thử đổi tên một trường và sau đó xem điều gì sẽ xảy ra.
Trejkaz

1
@grep quan điểm là nếu bạn có một lớp đã bỏ qua serialVersionUID trước đó, thì nó sẽ tự động nhận được lớp được tạo. Vì vậy, bây giờ, bạn muốn bắt đầu cài đặt nó một cách rõ ràng, đặt nó thành 1L sẽ làm cho nó không tương thích với lớp hiện có, trong khi sử dụng giá trị được tạo sẽ giữ cho nó tương thích.
marc82ch

15

serialVersionUIDGiá trị mặc định "dài" của giá trị mặc định như được định nghĩa bởi Đặc tả tuần tự hóa Java , được tính từ hành vi tuần tự hóa mặc định.

Vì vậy, nếu bạn thêm số phiên bản mặc định, lớp của bạn sẽ (hủy) tuần tự hóa nhanh hơn miễn là không có gì thay đổi về mặt cấu trúc, nhưng bạn phải lưu ý rằng nếu bạn thay đổi lớp (thêm / xóa trường), bạn cũng cập nhật số sê-ri.

Nếu bạn không phải tương thích với các luồng bit hiện có, bạn chỉ cần đặt 1Lở đó và tăng phiên bản khi cần khi có gì đó thay đổi. Đó là, khi phiên bản tuần tự hóa mặc định của lớp đã thay đổi sẽ khác với phiên bản mặc định của lớp cũ.


11

Bạn hoàn toàn nên tạo một serialVersionUID mỗi khi bạn xác định một lớp thực hiện java.io.Serializable. Nếu bạn không, một cái sẽ được tạo tự động cho bạn, nhưng điều này thật tệ. SerialVersionUID được tạo tự động dựa trên chữ ký phương thức của lớp của bạn, vì vậy nếu bạn thay đổi lớp trong tương lai để thêm một phương thức (ví dụ), việc khử lưu trữ các phiên bản "cũ" của lớp sẽ thất bại. Đây là những gì có thể xảy ra:

  1. Tạo phiên bản đầu tiên của lớp mà không cần xác định serialVersionUID.
  2. Tuần tự hóa một thể hiện của lớp của bạn đến một cửa hàng liên tục; một serialVersionUID được tạo tự động cho bạn.
  3. Sửa đổi lớp của bạn để thêm một phương thức mới và triển khai lại ứng dụng của bạn.
  4. Cố gắng giải tuần tự hóa cá thể đã được tuần tự hóa trong bước 2, nhưng bây giờ nó thất bại (khi nó thành công), bởi vì nó có một serialVersionUID được tạo tự động khác.

1
Như một vấn đề của thực tế, việc giải trừ các phiên bản cũ của lớp nên thực sự thất bại vì chúng không còn giống nhau nữa. Bạn đề nghị tự tạo serialVersionUID để ngăn chặn (de) lỗi tuần tự hóa khi chữ ký lớp thay đổi. Mặc dù đề xuất của bạn là phù hợp, nhưng lời giải thích của bạn về mục đích của nó chỉ đơn giản là sai và sai lệch. Sẽ là khôn ngoan để sửa đổi câu trả lời của bạn.
mostruash

6

Nếu bạn không chỉ định serialVersionUID thì Java sẽ tạo một cái ngay lập tức. Số serialVersionUID được tạo là số đó. Nếu bạn thay đổi thứ gì đó trong lớp không thực sự khiến lớp của bạn không tương thích với các câu đã được tuần tự hóa trước đó nhưng thay đổi hàm băm, thì bạn cần sử dụng serialVersionUID với số lượng rất lớn được tạo ra (hoặc số "mong đợi" từ thông báo lỗi) . Mặt khác, nếu bạn đang tự theo dõi mọi thứ, 0, 1, 2 ... sẽ tốt hơn.


Ý bạn là ==> 1. Nếu bạn muốn các thay đổi khác nhau của các lớp tương thích, hãy sử dụng lớp được tạo. 2. Nếu bạn muốn các phiên bản khác nhau của các lớp không tương thích, hãy sử dụng một phiên bản mặc định và thận trọng trong việc tăng dần. Tôi đã hiểu đúng chưa?
Nhà phát triển Java

4

Khi bạn sử dụng serialVersionUID (1L) thay vì tạo serialVersionUID (3567653491060394677L), bạn đang nói điều gì đó.

Bạn đang nói rằng bạn tự tin 100% rằng sẽ không có hệ thống nào chạm vào lớp này có phiên bản nối tiếp không tương thích của lớp này với số phiên bản là 1.

Nếu bạn có thể nghĩ ra bất kỳ lý do nào cho lịch sử phiên bản nối tiếp của nó là không xác định, điều đó có thể khó nói với sự tự tin. Trong vòng đời của nó, một lớp thành công sẽ được duy trì bởi nhiều người, sống trong nhiều dự án và cư trú trong nhiều hệ thống.

Bạn có thể đau đớn về điều đó. Hoặc bạn có thể chơi xổ số hy vọng sẽ thua. Nếu bạn tạo phiên bản, bạn có rất ít khả năng xảy ra sự cố. Nếu bạn giả sử "Hey tôi cá là chưa có ai sử dụng 1" thì tỷ lệ cược của bạn lớn hơn rất nhỏ. Đó chính xác là bởi vì tất cả chúng ta đều nghĩ 0 và 1 thật tuyệt vời khi bạn có tỷ lệ trúng chúng cao hơn.

-

Khi bạn tạo serialVersionUID (3567653491060394677L) thay vì sử dụng serialVersionUID (1L), bạn đang nói điều gì đó.

Bạn đang nói rằng mọi người có thể đã tạo thủ công hoặc tạo các số phiên bản khác trong lịch sử của lớp này và bạn không quan tâm vì Longs là những con số lớn đáng sợ.

Dù bằng cách nào, trừ khi bạn hoàn toàn biết lịch sử của các số phiên bản được sử dụng khi tuần tự hóa lớp trong toàn bộ vũ trụ nơi nó đã hoặc sẽ tồn tại, bạn sẽ có cơ hội. Nếu bạn có thời gian để chắc chắn 100% là AOK, hãy tiếp tục. Nếu đó là công việc nhiều, hãy tiếp tục và mù quáng tạo ra số. Bạn có nhiều khả năng trúng xổ số hơn là có sai lầm. Nếu vậy, hãy cho tôi biết và tôi sẽ mua cho bạn một cốc bia.

Với tất cả các cuộc nói chuyện về việc chơi xổ số này, tôi có thể đã cho bạn cảm giác rằng serialVersionUID được tạo ngẫu nhiên. Trong thực tế, miễn là phạm vi số được phân bổ đều trên mọi giá trị có thể của một Long sẽ ổn. Tuy nhiên, nó thực sự được thực hiện theo cách này:

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

Sự khác biệt duy nhất bạn có với đó là bạn không cần một nguồn ngẫu nhiên. Bạn đang sử dụng các thay đổi trong chính lớp để thay đổi kết quả. Nhưng theo nguyên tắc pigeonhole, vẫn có khả năng nó có thể đi sai và va chạm. Nó cực kỳ khó xảy ra. Vì vậy, chúc may mắn nhận được bia từ tôi.

Tuy nhiên, ngay cả khi lớp sẽ chỉ sống trong một hệ thống và một cơ sở mã, việc nghĩ rằng việc tăng số bằng tay sẽ không có cơ hội va chạm chỉ có nghĩa là bạn không hiểu con người. :)


Nếu một "hệ thống" chạm vào lớp, tức là thay đổi lớp theo cách mà việc tuần tự hóa trở nên không tương thích thì câu hỏi đặt ra là hệ thống đó cũng sẽ thay đổi serialVersionUID. Tôi không nghĩ rằng tỷ lệ cược nhỏ hơn mà nó sẽ nhớ để thay đổi nó khi nó còn dài. Tôi nghĩ đúng hơn là ngược lại, nếu con số dễ nhớ hơn thì những thay đổi cao hơn mà tôi nhận thấy rằng tôi đã vô tình không thay đổi nó.
Reto Gmür

2
Điều này là sai! Khi bạn tạo serialVersionUID và khai báo giá trị đó trong mã nguồn của bạn, thay vì 1L hoặc không có gì bạn thực sự nói: Tôi muốn một sự va chạm có thể không bị phát hiện trong tương lai với các hiệu ứng không xác định và tôi không muốn java hoặc bất kỳ con người nào ngăn chặn điều này . Java thì hoang tưởng, nhưng ngoan ngoãn. Con người thường không gây rối với số lượng lớn. Theo cách này, khi lớp thay đổi, java vẫn có thể hủy tuần tự hóa các phiên bản cũ không tương thích của nó. MwoaHaHa ...;)
Superole 17/12/14

1

Chà, serialVersionUID là một ngoại lệ đối với quy tắc rằng các trường tĩnh không nhận được serialized. ObjectOutputStream ghi mỗi khi giá trị của serialVersionUID vào luồng đầu ra. ObjectInputStream đọc lại và nếu giá trị đọc từ luồng không đồng ý với giá trị serialVersionUID trong phiên bản hiện tại của lớp, thì nó sẽ ném UnlimitedClassException. Hơn nữa, nếu không có serialVersionUID được khai báo chính thức trong lớp sẽ được tuần tự hóa, trình biên dịch sẽ tự động thêm nó với một giá trị được tạo dựa trên các trường được khai báo trong lớp.


0

Bởi vì trong nhiều trường hợp id mặc định không phải là duy nhất. vì vậy chúng tôi tạo id để tạo ra khái niệm độc đáo.


Bạn có thể chỉnh sửa câu trả lời của bạn để xác thịt nó nhiều hơn? Điều này có vẻ như một bình luận ở đây. Cảm ơn.
Xám

0

Để thêm vào câu trả lời @David Schmitts, theo nguyên tắc thông thường, tôi sẽ luôn sử dụng 1L mặc định ngoài quy ước. Tôi chỉ phải quay lại và thay đổi một vài trong số chúng một vài lần, nhưng tôi biết rằng khi tôi thực hiện thay đổi và cập nhật số mặc định mỗi lần.

Tại công ty hiện tại của tôi, họ yêu cầu số được tạo tự động nên tôi sử dụng số đó cho quy ước, nhưng tôi thích mặc định hơn. Tôi nghĩ là, nếu đó không phải là một quy ước nơi bạn làm việc, hãy sử dụng mặc định, trừ khi bạn nghĩ rằng bạn sẽ liên tục thay đổi cấu trúc của các lớp được xê-ri hóa vì một số lý do.


0

Mục đích của phiên bản tuần tự hóa UID là theo dõi các phiên bản khác nhau của một lớp để thực hiện tuần tự hóa các đối tượng hợp lệ.

Ý tưởng là tạo một ID duy nhất cho một phiên bản nhất định của một lớp, sau đó được thay đổi khi có các chi tiết mới được thêm vào lớp, chẳng hạn như một trường mới, sẽ ảnh hưởng đến cấu trúc của đối tượng được tuần tự hóa.

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

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.

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).

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

Nếu vậy, đặt serialVersionUID = 1L.

Đâ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 về serialVersionUID, và nên xem xét sâu về 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ũ.

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.