Dlaczego Java potrzebuje interfejsu szeregowego?

114

Ciężko pracujemy nad serializacją i konieczność określenia znacznika Serializable na każdym używanym przez nas obiekcie jest pewnym obciążeniem. Zwłaszcza, gdy jest to klasa zewnętrzna, której tak naprawdę nie możemy zmienić.

Pytanie brzmi: ponieważ Serializable jest pustym interfejsem, a Java zapewnia solidną serializację po dodaniu implements Serializable - dlaczego nie uczyniły wszystkiego możliwym do serializacji i to wszystko?

czego mi brakuje?

Yoni Roit
źródło
Co jeśli chcesz, aby Twój własny obiekt był serializowalny? A może coś źle zrozumiałem?
Joe Phillips
Nadal będę otrzymywać NotSerializableException, ponieważ wszystkie pola moich obiektów muszą być serializowane
Yoni Roit
Całkowicie zgadzam się z Pop Catalinem i Dmitry'm: „Ta sztuczka, którą można serializować, to tylko jedna z niewłaściwych decyzji, która została podjęta dekadę lub dwie temu”. Nie ma znaczenia, jak niebezpieczna może być serializacja. I nie jest prawdą, że skoro deklaracja jest wyraźna, to „wiesz, że musisz zwrócić szczególną uwagę”: każdy, kto tego potrzebuje, najpierw umieszcza rzeczy „implementuje”, a następnie myśli o implikacjach, jeśli coś poszło nie tak. Wszystko mogłoby być jaśniejsze, gdyby dali nam interfejs „Unserializable” do zastosowania w specjalnych przypadkach.
Jack

Odpowiedzi:

120

Serializacja jest pełna pułapek. Obsługa automatycznej serializacji tego formularza sprawia, że ​​elementy wewnętrzne klasy są częścią publicznego interfejsu API (dlatego javadoc udostępnia utrwalone formy klas ).

Aby zapewnić długotrwałą trwałość, klasa musi mieć możliwość dekodowania tego formularza, co ogranicza zmiany, które można wprowadzić w projekcie klasy. To przerywa hermetyzację.

Serializacja może również prowadzić do problemów z bezpieczeństwem. Dzięki możliwości serializacji dowolnego obiektu, do którego się odwołuje, klasa może uzyskać dostęp do danych, do których normalnie nie byłaby w stanie (poprzez analizę wynikowych danych bajtowych).

Istnieją inne problemy, takie jak serializowana forma klas wewnętrznych, która nie jest dobrze zdefiniowana.

Umożliwienie serializacji wszystkich klas zaostrzyłoby te problemy. Sprawdź Effective Java Second Edition , w szczególności Item 74: Implement Serializable rozsądnie .

McDowell
źródło
2
To jest zdecydowanie lepsza odpowiedź, jestem rozczarowany, że wybrana odpowiedź nie jest taka, wydaje się, że post wybrał z planem „Jestem zirytowany, ponieważ muszę zadeklarować rzeczy, które można serializować”. Jest to przykład kogoś, kto chce uwolnić środowisko i nie zwraca uwagi na lekcje bezpieczeństwa i projektowania, które zostały już wyciągnięte w przeszłości.
gbtimmon
@McDowell co masz na myśli mówiąc o utrwalonej formie zajęć? Skorzystałem z tego linku, ale nie rozumiem, co masz na myśli? czy możesz to wyjaśnić?
Geek
@Geek - serializowana postać typu adresu URL (na przykład) określa, jakie pola prywatne musi mieć typ i w jakiej kolejności muszą być zadeklarowane.
McDowell
@McDowell Dlaczego zamówienie ma znaczenie?
Geek
12
@McDowell Hi. Jestem oryginalnym plakatem tego pytania sprzed 4 lat i właśnie wybrałem Twoją odpowiedź jako zaakceptowaną, jeśli to cokolwiek znaczy. Myślę, że twoja odpowiedź jest naprawdę lepsza i prawdopodobnie byłem zbyt niedojrzały, żeby to widzieć w tamtym czasie. Naprawianie teraz :)
Yoni Roit
32

Myślę, że tym razem zarówno Java, jak i .Net pomyliły się, lepiej byłoby uczynić wszystko domyślnie możliwym do serializacji i zamiast tego wystarczy zaznaczyć te klasy, których nie można bezpiecznie serializować.

Na przykład w Smalltalk (języku stworzonym w latach 70.) każdy obiekt jest domyślnie możliwy do serializacji. Nie mam pojęcia, dlaczego tak nie jest w Javie, biorąc pod uwagę fakt, że zdecydowana większość obiektów można bezpiecznie serializować, a tylko kilka z nich nie.

Oznaczenie obiektu jako możliwego do serializacji (za pomocą interfejsu) nie czyni tego obiektu w magiczny sposób możliwym do serializacji, był on możliwy do serializacji przez cały czas , po prostu teraz wyraziłeś coś, co system mógłby znaleźć samodzielnie, więc nie widzę żadnego dobrego powodu, aby serializacja jest taka, jaka jest teraz.

Myślę, że była to zła decyzja podjęta przez projektantów, albo serializacja była po namyśle, albo platforma nigdy nie była gotowa do domyślnej serializacji wszystkich obiektów w sposób bezpieczny i spójny.

Pop Catalin
źródło
26
Podczas projektowania i implementowania klasy należy zachować szczególną ostrożność, aby upewnić się, że wystąpienia będą serializowane w rozsądny sposób. Interfejs możliwy do serializacji w rzeczywistości oznacza: „Ja, jako programista, zrozumiałem konsekwencje serializacji i zezwalam JVM na serializowanie tego”
Rolf Rander
@Rolf Rander: przez większość czasu nie musisz w ogóle się przejmować, po prostu zaznacz klasę jako możliwą do serializacji. Gdyby serializacja była domyślnie WŁĄCZONA na wszystkich obiektach, sposób myślenia każdego programisty również byłby inny, byłoby czymś naturalnym, aby uczynić klasę możliwą do serializacji ...
Pop Catalin
spowodowałoby to dodanie kolejnego problemu do listy dobrych projektów klas, na przykład upewnienie się, że nie masz wycieków pamięci. Mimo to dobry projekt klas coraz bardziej wymaga, aby klasy były serializowane, gdy tylko jest to możliwe.
Pop Catalin
3
@StaxMan, ponieważ uświadomienie sobie później, że musisz serializować klasę, a nie możesz, może być bardzo kosztowne. To jeden z tych przypadków, w których niewielki dodatkowy wysiłek się opłaca. Jest to szczególnie ważne, jeśli piszesz bibliotekę, a ktoś inny będzie ją konsumował bez kodu źródłowego
Pop Catalin
2
Całkowicie zgadzam się z tą odpowiedzią. W wielu językach wszystko jest domyślnie możliwe do serializacji. I właściwie to samo z JVM, ponieważ Reflection jest łatwy w użyciu, aby uzyskać dostęp do dowolnego członka klasy, niezależnie od tego, czy jest on prywatny, czy nie. Ta Serializablesztuczka to po prostu kolejna zła decyzja podjęta dekadę lub dwie temu i jeszcze jeden dodatek do irytacji podczas pracy z czystą Javą, jak niektóre błędy w zbieraniu i przetwarzaniu ciągów w standardowej bibliotece. Na szczęście istnieje Kryo, ale jest to zależność i trzeba ją najpierw znaleźć. W ten sposób powinna zostać wykonana wbudowana serializacja.
dmitry
20

Nie wszystko jest rzeczywiście możliwe do serializacji. Weźmy na przykład połączenie sieciowe. Możesz serializować dane / stan obiektu gniazda, ale istota aktywnego połączenia zostanie utracona.

Joel Coehoorn
źródło
To byłby mój problem i gdybym nie był wystarczająco sprytny, aby spróbować serializować gniazdo, znalazłbym swój błąd podczas debugowania. Jednak teraz jestem w sytuacji, w której nie mogę w ogóle używać serializacji Java z powodu klasy innej firmy, która nie implementuje Serializable bez powodu.
Yoni Roit
4
Istnieje kilka przyzwoitych sposobów rozwiązania tego przypadku, takich jak napisanie klasy opakowującej Serializable, która wie, jak odczytywać i zapisywać kluczowe dane klasy innej firmy; sprawia, że ​​opakowane wystąpienie jest przejściowe i zastępuje parametry writeObject i readObject.
Greg Case
Czy możesz dziedziczyć z wymaganych obiektów w swoim interfejsie API i serializować te klasy?
Joel Coehoorn
@Joel: To dobry pomysł, ale wciąż hack. Myślę, że to wszystko to po prostu kolejny kompromis poszedł nie tak. Dziękuję za uwagi.
Yoni Roit,
1
To jeden z tych rzadkich przykładów, który ilustruje, że wszystko, czego naprawdę potrzebujemy, to implements NotSerializable:)
Rob Grant
13

Główną rolą programu Serializable w Javie jest domyślne uniemożliwienie wszystkim innym obiektom serializacji. Serializacja to bardzo niebezpieczny mechanizm, zwłaszcza w domyślnej implementacji. Dlatego, podobnie jak przyjaźń w C ++, jest ona domyślnie wyłączona, nawet jeśli jej serializowanie kosztuje trochę.

Serializacja dodaje ograniczenia i potencjalne problemy, ponieważ zgodność struktury nie jest zapewniona. Dobrze, że jest domyślnie wyłączone.

Muszę przyznać, że widziałem bardzo niewiele nietrywialnych klas, w których standardowa serializacja robi to, co chcę. Zwłaszcza w przypadku złożonych struktur danych. Tak więc wysiłek, który trzeba poświęcić na poprawne serializowanie klasy, przewyższa koszt dodania interfejsu.

Uri
źródło
9

W przypadku niektórych klas, szczególnie tych, które reprezentują coś bardziej fizycznego, jak plik, gniazdo, wątek lub połączenie z bazą danych, serializowanie wystąpień nie ma absolutnie sensu. Dla wielu innych serializacja może być problematyczna, ponieważ niszczy ograniczenia związane z unikalnością lub po prostu zmusza do radzenia sobie z wystąpieniami różnych wersji klasy, czego możesz nie chcieć.

Prawdopodobnie lepiej byłoby uczynić wszystko domyślnie możliwym do serializacji i uczynić klasy nieserializowalnymi za pomocą słowa kluczowego lub interfejsu znacznika - ale wtedy ci, którzy powinni używać tej opcji, prawdopodobnie nie pomyśleliby o tym. Tak jest, jeśli musisz zaimplementować Serializable, zostaniesz o tym poinformowany przez wyjątek.

Michael Borgwardt
źródło
4

Myślę, że chodziło o to, abyś jako programista wiedział, że Twój obiekt może zostać zserializowany.

Milhous
źródło
1

Konieczność wyraźnego stwierdzenia, że ​​instancje określonej klasy są serializowalne, język zmusza do przemyślenia, jeśli powinieneś na to zezwolić. W przypadku prostych obiektów wartości serializacja jest banalna, ale w bardziej złożonych przypadkach trzeba naprawdę przemyśleć sprawę.

Po prostu polegając na standardowej obsłudze serializacji JVM, narażasz się na wszelkiego rodzaju nieprzyjemne problemy z wersjonowaniem.

Wyjątkowość, odniesienia do „rzeczywistych” zasobów, liczników czasu i wielu innych typów artefaktów NIE są kandydatami do serializacji.

Jeroen van Bergen
źródło
0

Cóż, odpowiadam, że nie bez powodu. Z twoich komentarzy widzę, że już się tego nauczyłeś. Inne języki szczęśliwie próbują serializować wszystko, co nie przeskakuje na drzewo po policzeniu do 10. Obiekt powinien domyślnie być serializowalny.

Zasadniczo musisz samodzielnie przeczytać wszystkie właściwości swojej klasy zewnętrznej. Lub, jeśli to dla ciebie opcja: dekompiluj, umieść tam słowo kluczowe i przekompiluj.

nes1983
źródło
2
Serializacja dodaje ograniczenia i potencjalne problemy, ponieważ zgodność struktury nie jest zapewniona. IMHO, dobrze, że jest domyślnie wyłączone.
Uri
Nie jestem pewien, co masz na myśli mówiąc o „zgodności konstrukcji”.
nes1983
0

W Javie są pewne rzeczy, których po prostu nie można serializować, ponieważ są one specyficzne dla środowiska wykonawczego. Rzeczy takie jak strumienie, wątki, środowisko uruchomieniowe itp., A nawet niektóre klasy GUI (które są połączone z podstawowym systemem operacyjnym) nie mogą być serializowane.


źródło
0

Chociaż zgadzam się z punktami przedstawionymi w innych odpowiedziach tutaj, prawdziwym problemem jest deserializacja: jeśli zmieni się definicja klasy, istnieje realne ryzyko, że deserializacja nie zadziała. Nigdy nie modyfikowanie istniejących pól jest dość dużym zobowiązaniem dla autora biblioteki! Utrzymanie zgodności z interfejsem API i tak jest wystarczającym obowiązkiem.

CurtainDog
źródło
To nie jest poprawne. Musisz przeczytać rozdział Versioning of Serializable Objects w specyfikacji serializacji obiektu, który nie istniałby, gdyby to, co tu twierdzisz, było prawdą. Definicja klasy może się zmienić w dość szerokich granicach, zanim stanie się niezgodna z wcześniejszymi serializacjami. I na pewno nie jest to odpowiedź na to pytanie. Prawdziwy powód wiąże się z obawami dotyczącymi bezpieczeństwa.
Markiz Lorne
@EJP, w trosce o prawdziwość zredagowałem moją odpowiedź, aby odzwierciedlić Twoje uwagi. Ten sentyment się jednak nie
zmienia
0

Klasa, która musi zostać utrwalona w pliku lub innym nośniku, musi implementować interfejs Serializable, aby maszyna JVM mogła umożliwić serializację obiektu klasy. Dlaczego klasa Object nie jest serializowana, żadna z klas nie musi implementować interfejsu, w końcu JVM serializuje klasę tylko wtedy, gdy używam ObjectOutputStream, co oznacza, że ​​kontrola jest nadal w moich rękach, aby pozwolić JVM na serializację.

Powodem, dla którego klasa Object nie jest domyślnie serializowalna, jest fakt, że wersja klasy jest głównym problemem. Dlatego każda klasa, która jest zainteresowana serializacją, musi być jawnie oznaczona jako Serializable i podać numer wersji serialVersionUID.

Jeśli serialVersionUID nie zostanie podany, otrzymamy nieoczekiwane wyniki podczas deserialzowania obiektu, dlatego JVM zgłasza InvalidClassException, jeśli serialVersionUID nie pasuje. Dlatego każda klasa musi zaimplementować interfejs Serializable i zapewnić serialVersionUID, aby upewnić się, że klasa prezentowana na obu końcach jest identyczna.

Trinesh Andhurthi
źródło