Warum benötigt Java eine serialisierbare Schnittstelle?


114

Wir arbeiten intensiv mit der Serialisierung und die Angabe eines serialisierbaren Tags für jedes von uns verwendete Objekt ist eine Art Belastung. Besonders wenn es sich um eine Klasse von Drittanbietern handelt, die wir nicht wirklich ändern können.

Die Frage ist: Da Serializable eine leere Schnittstelle ist und Java nach dem Hinzufügen eine robuste Serialisierung bietet implements Serializable- warum haben sie nicht alles serialisierbar gemacht und das war's?

Was vermisse ich?


Was ist, wenn Sie Ihr eigenes Objekt serialisierbar machen möchten? Oder habe ich etwas falsch verstanden?
Joe Phillips

Ich werde weiterhin NotSerializableException erhalten, da alle Felder meiner Objekte serialisierbar sein müssen
Yoni Roit

Stimmen Sie voll und ganz mit Pop Catalin und dmitry überein. "Dieser serialisierbare Trick ist nur eine weitere falsche Entscheidung, die vor ein oder zwei Jahrzehnten getroffen wurde." Es spielt keine Rolle, wie gefährlich Serialisierung sein kann. Und es ist nicht wahr, dass, da die Erklärung explizit ist, "Sie wissen, dass Sie besondere Aufmerksamkeit schenken müssen": Jeder, der sie benötigt, setzt zuerst "Geräte" ein und denkt dann über Implikationen nach, wenn etwas schief gelaufen ist. Es hätte klarer sein können, wenn sie uns eine "Unserialisierbare" Schnittstelle gegeben hätten, die auf Sonderfälle angewendet werden kann.
Jack

Antworten:


120

Die Serialisierung ist mit Fallstricken behaftet. Automatische Serialisierung Unterstützung dieser Form macht die Klasse Interna Teil der öffentlichen API (weshalb javadoc Sie gibt beharrte Formen der Klassen ).

Für eine langfristige Persistenz muss die Klasse in der Lage sein, dieses Formular zu dekodieren, wodurch die Änderungen, die Sie am Klassendesign vornehmen können, eingeschränkt werden. Dies unterbricht die Kapselung.

Die Serialisierung kann auch zu Sicherheitsproblemen führen. Durch die Serialisierung eines Objekts, auf das verwiesen wird, kann eine Klasse auf Daten zugreifen, auf die sie normalerweise nicht zugreifen kann (durch Parsen der resultierenden Byte-Daten).

Es gibt andere Probleme, wie zum Beispiel, dass die serialisierte Form der inneren Klassen nicht genau definiert ist.

Die Serialisierbarkeit aller Klassen würde diese Probleme verschlimmern. Schauen Sie sich Effective Java Second Edition an , insbesondere Punkt 74: Serializable mit Bedacht implementieren .


2
Dies ist eindeutig die bessere Antwort. Ich bin enttäuscht, dass die ausgewählte Antwort nicht die folgende ist. Es scheint, dass die veröffentlichte Antwort mit der Agenda "Ich bin verärgert, weil ich Dinge für serialisierbar erklären muss" ausgewählt wurde. Es ist ein Beispiel für jemanden, der sich frei machen möchte und die Sicherheits- und Designlektionen, die bereits in der Vergangenheit gelernt wurden, nicht beachtet.
Gbtimmon

@McDowell was meinst du mit persistierter Form von Klassen? Ich bin diesem Link gefolgt, kann aber nicht verstehen, was du meinst? Kannst du das bitte erklären?
Geek

@Geek - Die serialisierte Form des URL-Typs (zum Beispiel) definiert, welche privaten Felder der Typ haben muss und in welcher Reihenfolge sie deklariert werden müssen.
McDowell

@ McDowell Warum ist die Bestellung wichtig?
Geek

12
@ McDowell Hallo. Ich bin das Originalplakat dieser Frage von vor 4 Jahren und habe gerade Ihre Antwort als akzeptiert ausgewählt, wenn sie etwas bedeutet. Ich denke, Ihre Antwort ist wirklich die bessere, und ich war wahrscheinlich zu unreif, um es damals so zu sehen. Fixing now :)
Yoni Roit

32

Ich denke, sowohl Java- als auch .NET-Leute haben diesmal etwas falsch gemacht. Es wäre besser gewesen, alles standardmäßig serialisierbar zu machen und nur die Klassen zu markieren, die stattdessen nicht sicher serialisiert werden können.

In Smalltalk (einer Sprache, die in den 70er Jahren erstellt wurde) ist beispielsweise jedes Objekt standardmäßig serialisierbar. Ich habe keine Ahnung, warum dies in Java nicht der Fall ist, wenn man bedenkt, dass die überwiegende Mehrheit der Objekte sicher serialisiert werden kann und nur einige von ihnen nicht.

Das Markieren eines Objekts als serialisierbar (mit einer Schnittstelle) macht dieses Objekt nicht auf magische Weise serialisierbar, es war die ganze Zeit serialisierbar . Es ist nur so, dass Sie jetzt etwas ausgedrückt haben, das das System selbst hätte finden können, also sehe ich keinen wirklich guten Grund dafür Serialisierung ist so wie sie jetzt ist.

Ich denke, es war entweder eine schlechte Entscheidung der Designer oder die Serialisierung war ein nachträglicher Gedanke, oder die Plattform war nie bereit, standardmäßig alle Objekte sicher und konsistent zu serialisieren.


26
Beim Entwerfen und Implementieren einer Klasse müssen Sie besonders vorsichtig sein, um sicherzustellen, dass Instanzen auf sinnvolle Weise serialisiert werden. Die serialisierbare Schnittstelle bedeutet tatsächlich: "Ich als Programmierer habe die Konsequenzen der Serialisierung verstanden und erlaube der JVM, diese zu serialisieren"
Rolf Rander

@Rolf Rander: Meistens müssen Sie sich überhaupt nicht darum kümmern, markieren Sie einfach die Klasse serialisierbar. Wenn die Serialisierung standardmäßig für alle Objekte aktiviert gewesen wäre, wäre auch die Denkweise jedes Entwicklers anders gewesen. Es wäre etwas Natürliches gewesen, eine Klasse serialisierbar zu machen ...
Pop Catalin

Dies hätte der Liste des guten Klassendesigns ein weiteres Problem hinzugefügt, z. B. sicherzustellen, dass Sie keine Speicherlecks haben. Dennoch erfordert ein gutes Klassendesign immer mehr, dass Ihre Klassen serialisierbar sind, wenn dies möglich ist.
Pop Catalin

3
@StaxMan, da es sehr kostspielig sein kann, später zu erkennen, dass Sie eine Klasse serialisieren müssen und dies nicht können. Dies ist einer der Fälle, in denen sich wenig zusätzlicher Aufwand auszahlt. Dies gilt insbesondere dann, wenn Sie eine Bibliothek schreiben und diese von jemand anderem ohne den Quellcode verwendet wird
Pop Catalin

2
Ich stimme dieser Antwort voll und ganz zu. In vielen Sprachen ist standardmäßig alles serialisierbar. Und genau das Gleiche gilt für JVM, da Reflection einfach verwendet werden kann, um auf jedes Klassenmitglied zuzugreifen, unabhängig davon, ob es privat ist oder nicht. Dieser SerializableTrick ist nur eine weitere falsche Entscheidung, die vor ein oder zwei Jahrzehnten getroffen wurde, und eine weitere Ergänzung zum Ärger beim Umgang mit reinem Java, wie einige Fehler bei der Sammlung und Zeichenfolgenverarbeitung in der Standardbibliothek. Glücklicherweise gibt es Kryo, aber es ist Abhängigkeit und man muss es zuerst finden. So hätte die eingebaute Serialisierung erfolgen sollen.
dmitry

20

Nicht alles ist wirklich serialisierbar. Nehmen Sie zum Beispiel eine Netzwerk-Socket-Verbindung. Sie könnten die Daten / den Status Ihres Socket-Objekts serialisieren, aber das Wesentliche einer aktiven Verbindung würde verloren gehen.


Dies wäre mein Problem, und wenn ich nicht klug genug wäre, um zu versuchen, den Socket zu serialisieren, würde ich meinen Fehler beim Debuggen finden. Jetzt bin ich jedoch in einer Situation, in der ich die Java-Serialisierung aufgrund einer Klasse von Drittanbietern, die Serializable nicht ohne guten Grund implementiert, überhaupt nicht verwenden kann.
Yoni Roit

4
Es gibt einige anständige Möglichkeiten, um diesen Fall zu behandeln, z. B. das Schreiben einer serialisierbaren Wrapper-Klasse, die weiß, wie die Schlüsseldaten der Klasse von Drittanbietern gelesen und geschrieben werden. Machen Sie die umschlossene Instanz vorübergehend und überschreiben Sie writeObject und readObject.
Greg Case

Können Sie von den erforderlichen Objekten in Ihrer API erben und diese Klassen serialisieren?
Joel Coehoorn

@ Joel: Es ist eine gute Idee, aber immer noch ein Hack. Ich denke, diese ganze Sache ist nur ein weiterer Kompromiss, der schief gelaufen ist. Danke für deine Kommentare.
Yoni Roit

1
Dies ist eines dieser seltenen Beispiele, die zeigen, dass alles, was wir wirklich brauchen, ist implements NotSerializable:)
Rob Grant

13

Die Hauptaufgabe von Serializable in Java besteht darin, alle anderen Objekte standardmäßig nicht serialisierbar zu machen. Die Serialisierung ist ein sehr gefährlicher Mechanismus, insbesondere in der Standardimplementierung. Daher ist es wie die Freundschaft in C ++ standardmäßig deaktiviert, auch wenn es ein wenig kostet, Dinge serialisierbar zu machen.

Durch die Serialisierung werden Einschränkungen und potenzielle Probleme hinzugefügt, da die Strukturkompatibilität nicht gewährleistet ist. Es ist gut, dass es standardmäßig deaktiviert ist.

Ich muss zugeben, dass ich nur sehr wenige nichttriviale Klassen gesehen habe, in denen die Standardserialisierung das tut, was ich will. Insbesondere bei komplexen Datenstrukturen. Der Aufwand für die ordnungsgemäße Serialisierung der Klasse erhöht also die Kosten für das Hinzufügen der Schnittstelle.


9

Für einige Klassen, insbesondere für Klassen, die etwas Physikalischeres wie eine Datei, einen Socket, einen Thread oder eine DB-Verbindung darstellen, ist es absolut sinnlos, Instanzen zu serialisieren. Für viele andere kann die Serialisierung problematisch sein, da sie die Eindeutigkeitsbeschränkungen aufhebt oder Sie einfach dazu zwingt, sich mit Instanzen verschiedener Versionen einer Klasse zu befassen, die Sie möglicherweise nicht möchten.

Möglicherweise war es besser, alles standardmäßig serialisierbar zu machen und Klassen über ein Schlüsselwort oder eine Markierungsschnittstelle nicht serialisierbar zu machen - aber diejenigen, die diese Option verwenden sollten, würden wahrscheinlich nicht darüber nachdenken. So wie es ist, wenn Sie Serializable implementieren müssen, wird Ihnen dies durch eine Ausnahme mitgeteilt.


4

Ich denke, das war, um sicherzustellen, dass Sie als Programmierer wissen, dass Ihr Objekt serialisiert werden kann.



1

Wenn Sie explizit angeben müssen, dass Instanzen einer bestimmten Klasse serialisierbar sind, müssen Sie darüber nachdenken, ob Sie dies zulassen sollten. Für einfache Wertobjekte ist die Serialisierung trivial, aber in komplexeren Fällen müssen Sie die Dinge wirklich durchdenken.

Indem Sie sich nur auf die Standard-Serialisierungsunterstützung der JVM verlassen, setzen Sie sich allen möglichen bösen Versionsproblemen aus.

Einzigartigkeit, Verweise auf "echte" Ressourcen, Timer und viele andere Arten von Artefakten sind KEINE Kandidaten für die Serialisierung.



0

Nun, meine Antwort ist, dass dies keinen guten Grund hat. Und aus Ihren Kommentaren kann ich ersehen, dass Sie das bereits gelernt haben. Andere Sprachen versuchen gerne, alles zu serialisieren, was nicht auf einen Baum springt, nachdem Sie bis 10 gezählt haben. Ein Objekt sollte standardmäßig serialisierbar sein.

Sie müssen also grundsätzlich alle Eigenschaften Ihrer Drittanbieter-Klasse selbst lesen. Oder, wenn dies eine Option für Sie ist: dekompilieren, das verdammte Schlüsselwort dort einfügen und neu kompilieren.


2
Durch die Serialisierung werden Einschränkungen und potenzielle Probleme hinzugefügt, da die Strukturkompatibilität nicht gewährleistet ist. IMHO, es ist gut, dass es standardmäßig deaktiviert ist.
Uri

Ich bin mir nicht sicher, was Sie unter "Strukturkompatibilität" verstehen.
nes1983

0

Es gibt einige Dinge in Java, die einfach nicht serialisiert werden können, weil sie laufzeitspezifisch sind. Dinge wie Streams, Threads, Laufzeit usw. und sogar einige GUI-Klassen (die mit dem zugrunde liegenden Betriebssystem verbunden sind) können nicht serialisiert werden.


0

Obwohl ich den Punkten in anderen Antworten hier zustimme, besteht das eigentliche Problem in der Deserialisierung: Wenn sich die Klassendefinition ändert, besteht ein echtes Risiko, dass die Deserialisierung nicht funktioniert. Niemals vorhandene Felder zu ändern, ist für den Autor einer Bibliothek eine ziemlich große Verpflichtung! Die Aufrechterhaltung der API-Kompatibilität ist eine mühsame Aufgabe.


Das ist nicht richtig. Sie müssen das Kapitel Versionierung serialisierbarer Objekte in der Objektserialisierungsspezifikation lesen , das nicht existieren würde, wenn das, was Sie hier behaupten, wahr wäre. Die Klassendefinition kann sich in ziemlich großen Grenzen ändern, bevor sie mit früheren Serialisierungen nicht mehr kompatibel ist. Und es ist sicherlich nicht die Antwort auf die Frage. Der wahre Grund hat mit Sicherheitsbedenken zu tun.
Marquis von Lorne

@EJP, im Interesse der Wahrhaftigkeit habe ich meine Antwort bearbeitet, um Ihre Punkte widerzuspiegeln. Die Stimmung steht jedoch, es ist eine große Verpflichtung, die definitiv eher Opt-In als Opt-Out sein sollte
CurtainDog

0

Eine Klasse, die für eine Datei oder ein anderes Medium beibehalten werden muss, muss eine serialisierbare Schnittstelle implementieren, damit JVM die Serialisierung des Klassenobjekts ermöglichen kann. Warum die Objektklasse nicht serialisiert wird, muss keine der Klassen die Schnittstelle implementieren, schließlich serialisiert JVM die Klasse nur, wenn ich ObjectOutputStream verwende bedeutet, dass das Steuerelement immer noch in meinen Händen liegt, damit die JVM serialisiert werden kann.

Der Grund, warum die Objektklasse standardmäßig nicht serialisierbar ist, liegt darin, dass die Klassenversion das Hauptproblem darstellt. Daher muss jede Klasse, die an Serialisierung interessiert ist, explizit als serialisierbar markiert werden und eine Versionsnummer serialVersionUID angeben.

Wenn serialVersionUID nicht angegeben wird, erhalten wir beim Deserialisieren des Objekts unerwartete Ergebnisse. Aus diesem Grund löst JVM InvalidClassException aus, wenn serialVersionUID nicht übereinstimmt. Daher muss jede Klasse eine serialisierbare Schnittstelle implementieren und eine serialVersionUID bereitstellen , um sicherzustellen, dass die an beiden Enden dargestellte Klasse identisch ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.