Tại sao Java cần giao diện Serializable?


114

Chúng tôi làm việc nhiều với tuần tự hóa và việc phải chỉ định thẻ Serializable trên mọi đối tượng mà chúng tôi sử dụng là một gánh nặng. Đặc biệt khi đó là lớp của bên thứ 3 mà chúng tôi thực sự không thể thay đổi.

Câu hỏi đặt ra là: vì Serializable là một giao diện trống và Java cung cấp khả năng tuần tự hóa mạnh mẽ khi bạn thêm implements Serializablevào - tại sao họ không làm cho mọi thứ có thể tuần tự hóa và đó là nó?

Tôi đang thiếu gì?


Điều gì sẽ xảy ra nếu bạn muốn làm cho đối tượng của riêng bạn có thể Serializable? Hay tôi đã hiểu lầm điều gì đó?
Joe Phillips

Tôi vẫn sẽ nhận được NotSerializableException bởi vì tất cả các lĩnh vực của các đối tượng của tôi phải serializable
Yoni Roit

Hoàn toàn đồng ý với Pop Catalin và "Thủ thuật có thể nối tiếp này chỉ là một quyết định sai lầm khác đã được thực hiện cách đây một hoặc hai thập kỷ" của dmitry. Việc tuần tự hóa có thể nguy hiểm đến mức nào. Và không đúng vì khai báo là rõ ràng nên "bạn biết rằng bạn phải đặc biệt chú ý": tất cả những người cần nó trước tiên đặt những thứ "nông cụ", và sau đó nghĩ về hàm ý nếu có gì đó sai. Mọi chuyện có thể rõ ràng hơn nếu họ cung cấp cho chúng ta một giao diện "Không thể hóa được" để áp dụng cho các trường hợp đặc biệt.
Jack

Câu trả lời:


120

Serialization chứa đầy cạm bẫy. Hỗ trợ tuần tự hóa tự động của biểu mẫu này làm cho nội bộ lớp trở thành một phần của API công khai (đó là lý do tại sao javadoc cung cấp cho bạn các biểu mẫu lớp tồn tại lâu dài ).

Để tồn tại lâu dài, lớp phải có khả năng giải mã biểu mẫu này, điều này hạn chế những thay đổi bạn có thể thực hiện đối với thiết kế lớp. Điều này phá vỡ tính đóng gói.

Việc tuần tự hóa cũng có thể dẫn đến các vấn đề bảo mật. Bằng cách có thể tuần tự hóa bất kỳ đối tượng nào mà nó có tham chiếu đến, một lớp có thể truy cập dữ liệu mà nó thường không thể làm được (bằng cách phân tích cú pháp dữ liệu byte kết quả).

Có những vấn đề khác, chẳng hạn như dạng tuần tự của các lớp bên trong không được xác định rõ.

Làm cho tất cả các lớp có thể tuần tự hóa sẽ làm trầm trọng thêm những vấn đề này. Kiểm tra Phiên bản Java thứ hai hiệu quả , cụ thể là Mục 74: Triển khai Serializable một cách thận trọng .


2
Đây rõ ràng là câu trả lời tốt hơn, tôi thất vọng vì câu trả lời được chọn không phải là câu trả lời này, có vẻ như người đăng đã chọn với chương trình nghị sự "Tôi khó chịu vì tôi phải khai báo những điều có thể tuần tự hóa". Đó là một ví dụ về một số người muốn giải phóng whell và không chú ý đến các bài học bảo mật và thiết kế đã được học trong quá khứ.
gbtimmon

@McDowell cho bạn biết ý nghĩa của hình thức lớp học liên tục là gì? Tôi đã theo liên kết đó nhưng không thể hiểu ý bạn? bạn có thể giải thích điều này xin vui lòng?
Geek

@Geek - Biểu mẫu được tuần tự hóa của loại URL (ví dụ) xác định loại trường riêng tư nào phải có và thứ tự chúng phải được khai báo.
McDowell

@McDowell Tại sao đơn đặt hàng lại quan trọng?
Geek

12
@McDowell Xin chào. Tôi là người đăng ban đầu của câu hỏi này từ 4 năm trước, và tôi vừa chọn câu trả lời của bạn là được chấp nhận, nếu nó có ý nghĩa. Tôi nghĩ câu trả lời của bạn thực sự là câu trả lời hay hơn, và tôi có lẽ còn quá non nớt để nhìn nhận nó theo cách đó vào thời điểm đó. Sửa bây giờ :)
Yoni Roit

32

Tôi nghĩ rằng cả người Java và .Net lần này đều mắc lỗi, tốt hơn hết là nên đặt mọi thứ có thể tuần tự hóa theo mặc định và chỉ cần đánh dấu những lớp không thể tuần tự hóa một cách an toàn.

Ví dụ trong Smalltalk (một ngôn ngữ được tạo ra trong những năm 70) mọi đối tượng đều có thể tuần tự hóa theo mặc định. Tôi không biết tại sao điều này lại không xảy ra trong Java, vì thực tế là phần lớn các đối tượng đều an toàn để tuần tự hóa và chỉ một vài trong số chúng thì không.

Đánh dấu một đối tượng là có thể tuần tự hóa (với một giao diện) không làm cho đối tượng đó có thể được tuần tự hóa một cách kỳ diệu, nó hoàn toàn có thể được tuần tự hóa , chỉ là bây giờ bạn đã thể hiện điều gì đó mà hệ thống có thể tự tìm thấy, vì vậy tôi không thấy lý do chính đáng nào cho việc tuần tự hóa như bây giờ.

Tôi nghĩ rằng đó là một quyết định sai lầm của các nhà thiết kế hoặc tuần tự hóa là một suy nghĩ muộn màng, hoặc nền tảng này chưa bao giờ sẵn sàng thực hiện tuần tự hóa theo mặc định trên tất cả các đối tượng một cách an toàn và nhất quán.


26
Bạn cần đặc biệt chú ý khi thiết kế và triển khai một lớp để đảm bảo rằng các phiên bản sẽ được tuần tự hóa một cách hợp lý. Giao diện serializable thực sự có nghĩa là: "Tôi, như một lập trình viên, đã hiểu được hậu quả của serialization và giấy phép JVM để serialize này"
Rolf Rander

@Rolf Rander: hầu hết thời gian bạn không cần phải chăm sóc gì cả, chỉ cần đánh dấu lớp có thể tuần tự hóa. Nếu serialization sẽ được ON bởi defaul trên tất cả các đối tượng tư duy của mỗi nhà phát triển sẽ là khác nhau quá, nó đã có một cái gì đó tự nhiên để làm để làm cho một serializable lớp ...
Pop Catalin

nó sẽ thêm một mối quan tâm khác vào danh sách thiết kế lớp tốt, chẳng hạn như đảm bảo rằng bạn không bị rò rỉ bộ nhớ. Tuy nhiên, thiết kế lớp tốt ngày càng nhiều yêu cầu các lớp của bạn phải có thể tuần tự hóa khi chúng có thể.
Pop Catalin

3
@StaxMan, vì sau này nhận ra rằng bạn cần phải tuần tự hóa một lớp và bạn không thể, có thể rất tốn kém. Đó là một trong những trường hợp mà ít nỗ lực hơn cũng được đền đáp. Điều này đặc biệt đúng nếu bạn đang viết một thư viện và một người khác sẽ tiêu thụ nó mà không có mã nguồn
Pop Catalin

2
Tôi hoàn toàn đồng ý với câu trả lời này. Trong nhiều ngôn ngữ, mọi thứ đều có thể tuần tự hóa theo mặc định. Và thực tế với JVM cũng vậy, vì có thể dễ dàng sử dụng Reflection để truy cập vào bất kỳ thành viên nào trong lớp, cho dù đó là riêng tư hay không. Đây Serializablelừa chỉ là một trong một quyết định sai lầm đã được thực hiện hoặc hai thập kỷ trước, và thêm một Ngoài không ít phiền toái khi giao dịch với java tinh khiết, giống như một số sai sót trong việc thu thập và xử lý chuỗi trong thư viện chuẩn. Thật hạnh phúc khi có Kryo, nhưng đó là sự phụ thuộc và người ta cần phải tìm ra nó trước. Đây là cách tuần tự hóa tích hợp sẵn nên được thực hiện.
dmitry

20

Không phải tất cả mọi thứ đều có thể tuần tự hóa thực sự. Lấy ví dụ kết nối ổ cắm mạng. Bạn có thể tuần tự hóa dữ liệu / trạng thái của đối tượng socket của mình, nhưng bản chất của một kết nối đang hoạt động sẽ bị mất.


Đây sẽ là vấn đề của tôi và nếu tôi không đủ thông minh để cố gắng nối tiếp ổ cắm, tôi sẽ tìm thấy lỗi của mình trong quá trình gỡ lỗi. Tuy nhiên, bây giờ tôi đang ở trong một tình huống khi tôi không thể sử dụng tuần tự hóa Java do lớp bên thứ 3 không triển khai Serializable mà không có lý do chính đáng.
Yoni Roit

4
Có một số cách hợp lý để xử lý trường hợp này, chẳng hạn như viết một lớp trình bao bọc có thể nối tiếp hóa có thể biết cách đọc và ghi dữ liệu khóa của lớp bên thứ 3; làm cho phiên bản được bọc tạm thời và ghi đè writeObject và readObject.
Greg Case

Bạn có thể kế thừa từ các đối tượng được yêu cầu trong api của mình và tuần tự hóa các lớp đó không?
Joel Coehoorn

@Joel: Đó là một ý tưởng hay, nhưng vẫn là một bản hack. Tôi đoán toàn bộ điều này chỉ là một sự đánh đổi khác đã xảy ra. Cảm ơn cho ý kiến ​​của bạn.
Yoni Roit

1
Đây là một trong những ví dụ hiếm hoi mà minh họa rằng tất cả chúng ta thực sự cần là implements NotSerializable:)
Rob Grant

13

Vai trò chính của Serializable trong Java là thực sự làm cho tất cả các đối tượng khác trở nên vô nghĩa. Serialization là một cơ chế rất nguy hiểm, đặc biệt là trong cài đặt mặc định của nó. Do đó, giống như tình bạn trong C ++, nó bị tắt theo mặc định, ngay cả khi tốn một ít chi phí để làm cho mọi thứ có thể tuần tự hóa.

Việc tuần tự hóa thêm các ràng buộc và các vấn đề tiềm ẩn vì tính tương thích của cấu trúc không được bảo hiểm. Tốt là nó được tắt theo mặc định.

Tôi phải thừa nhận rằng tôi đã thấy rất ít lớp tầm thường nơi tuần tự hóa tiêu chuẩn thực hiện những gì tôi muốn. Đặc biệt là trong trường hợp cấu trúc dữ liệu phức tạp. Vì vậy, nỗ lực bạn bỏ ra để làm cho lớp có thể tuần tự hóa đúng cách sẽ giảm thiểu chi phí thêm giao diện.


9

Đối với một số lớp, đặc biệt là những lớp đại diện cho một cái gì đó vật lý hơn như Tệp, Socket, Luồng hoặc kết nối DB, việc tuần tự hóa các cá thể hoàn toàn không có ý nghĩa gì. Đối với nhiều người khác, Serialization có thể có vấn đề vì nó phá hủy các ràng buộc về tính duy nhất hoặc đơn giản là buộc bạn phải xử lý các phiên bản của các phiên bản khác nhau của một lớp mà bạn có thể không muốn.

Có thể cho rằng, tốt hơn hết là nên đặt mọi thứ có thể tuần tự hóa theo mặc định và làm cho các lớp không thể tuần tự hóa thông qua giao diện từ khóa hoặc điểm đánh dấu - nhưng sau đó, những người nên sử dụng tùy chọn đó có thể sẽ không nghĩ đến nó. Theo cách của nó, nếu bạn cần triển khai Serializable, bạn sẽ được thông báo như vậy bởi một Exception.


4

Tôi nghĩ rằng mặc dù vậy là để đảm bảo rằng bạn, với tư cách là lập trình viên, biết rằng đối tượng của bạn được tuần tự hóa.



1

Phải tuyên bố rõ ràng rằng các phiên bản của một lớp nhất định có thể được Serializable, ngôn ngữ buộc bạn phải suy nghĩ xem bạn có nên cho phép điều đó hay không. Đối với các đối tượng có giá trị đơn giản, việc tuần tự hóa là điều tầm thường, nhưng trong những trường hợp phức tạp hơn, bạn cần phải thực sự suy nghĩ thấu đáo.

Chỉ cần dựa vào sự hỗ trợ tuần tự hóa tiêu chuẩn của JVM, bạn có thể gặp phải tất cả các loại vấn đề khó chịu về phiên bản.

Tính duy nhất, tham chiếu đến tài nguyên 'thực', bộ đếm thời gian và rất nhiều loại hiện vật khác KHÔNG phải là ứng cử viên để đăng nhiều kỳ.



0

Vâng, câu trả lời của tôi là điều này không có lý do chính đáng. Và từ nhận xét của bạn, tôi có thể thấy rằng bạn đã học được điều đó. Các ngôn ngữ khác vui vẻ thử tuần tự hóa mọi thứ không nhảy trên cây sau khi bạn đã đếm đến 10. Một Đối tượng nên mặc định là có thể tuần tự hóa.

Vì vậy, về cơ bản những gì bạn cần làm là tự đọc tất cả các thuộc tính của lớp bên thứ 3 của mình. Hoặc, nếu đó là một lựa chọn cho bạn: dịch ngược, đặt từ khóa chết tiệt ở đó và biên dịch lại.


2
Việc tuần tự hóa thêm các ràng buộc và các vấn đề tiềm ẩn vì tính tương thích của cấu trúc không được bảo hiểm. IMHO, Thật tốt là nó được tắt theo mặc định.
Uri

Tôi không chắc ý của bạn về "khả năng tương thích cấu trúc".
nes1983

0

Có một số thứ trong Java đơn giản là không thể được tuần tự hóa bởi vì chúng là thời gian chạy cụ thể. Những thứ như luồng, luồng, thời gian chạy, v.v. và thậm chí một số lớp GUI (được kết nối với Hệ điều hành cơ bản) không thể được tuần tự hóa.


0

Mặc dù tôi đồng ý với các điểm được đưa ra trong các câu trả lời khác ở đây, nhưng vấn đề thực sự là với quá trình khử không khí: Nếu định nghĩa lớp thay đổi thì có nguy cơ thực sự là quá trình giải mã không hoạt động. Không bao giờ sửa đổi các trường hiện có là một cam kết khá lớn mà tác giả của một thư viện phải thực hiện! Duy trì tính tương thích của API là một việc vặt như hiện nay.


Điều này LAF không đúng. Bạn cần đọc chương Phiên bản của các đối tượng có thể tuần tự hóa của Đặc tả tuần tự hóa đối tượng, chương này sẽ không tồn tại nếu những gì bạn tuyên bố ở đây là đúng. Định nghĩa lớp có thể thay đổi trong giới hạn khá rộng trước khi nó trở nên không tương thích với các bản tuần tự trước. Và nó chắc chắn không phải là câu trả lời cho câu hỏi. Lý do thực sự liên quan đến những lo ngại về bảo mật.
Marquis of Lorne,

@EJP, vì sự tin cậy, tôi đã chỉnh sửa câu trả lời của mình để phản ánh quan điểm của bạn. Mặc dù vậy, tình cảm là một cam kết lớn chắc chắn nên chọn tham gia hơn là chọn không tham gia
CurtainDog

0

Một lớp cần được duy trì trong một tệp hoặc phương tiện khác phải triển khai giao diện có thể nối tiếp hóa, để JVM có thể cho phép đối tượng lớp được tuần tự hóa. Tại sao lớp Đối tượng không được tuần tự hóa thì không lớp nào cần phải triển khai giao diện, sau cùng thì JVM chỉ tuần tự hóa lớp khi tôi sử dụng ObjectOutputStream có nghĩa là quyền kiểm soát vẫn nằm trong tay tôi để cho JVM tuần tự hóa.

Lý do tại sao lớp Đối tượng không thể tuần tự hóa theo mặc định trong thực tế là phiên bản lớp là vấn đề chính. Do đó, mỗi lớp quan tâm đến việc tuần tự hóa phải được đánh dấu là Có thể tuần tự hóa một cách rõ ràng và cung cấp một số phiên bản serialVersionUID.

Nếu serialVersionUID không được cung cấp thì chúng tôi nhận được kết quả không mong muốn trong khi giải mã đối tượng, đó là lý do tại sao JVM ném ra InvalidClassException nếu serialVersionUID không khớp. Do đó, mọi lớp phải triển khai giao diện Serializable và cung cấp serialVersionUID để đảm bảo rằng Lớp được trình bày ở cả hai đầu là giống hệt nhau.

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.