Serialisierung

Serialisierung ermöglicht es, Objekte mehr oder weniger vollständig und verlustfrei zu speichern oder über das Netzwerk zu übertragen. So etwas konnte man schon immer machen, aber früher musste man die Objekte „von Hand“ serialisieren, also jeweils die Funktionalität schreiben, die das leistet. Auch wenn es damals noch nicht Objekte waren.

Java hatte dann plötzlich so eine Serialisierung für (fast) alle Objekte an Bord und man musste nur noch ObjectOutputStream und ObjectInputStream verwenden und konnte alle seine Objekte speichern und wieder zurücklesen. Sogar Referenzen, die mehrmals dasselbe Objekt referenzierten, wurden richtig behandelt. Neu war die Idee wohl nicht wirklich, andere Programmiersprachen konnten das schon viel früher, aber das hat kaum jemand gemerkt.

Dass dann doch eine gewisse Ernüchterung aufkam, soll hier nicht verschwiegen werden:

  • Die Markierung mit dem Serializiable-Interface ist konzeptionell eine schlechte Lösung, weil sie annimmt, dass abgeleitete Klassen automatisch auch serialisierbar sind, was nicht stimmt. Besser wäre ein Unserializable-Interface gewesen. Oder heute eine Annotation..
  • Mit dieser SerialVersionUID hat man eine Menge Ärger. Soll man sie immer nachführen, wenn sich das Interface ändert? Oder lieber den automatischen Mechanismen vertrauen? So oder so bleibt das Problem der inkompatiblen Versionen, das nicht wirklich gut gelöst und vielleicht auch nicht einfach lösbar ist.
  • Die Serialisierung bringt einen weiteren, verdeckten Konstruktor ins Spiel.
  • Die Serialisierung unterläuft private und protected, denn alles muss ja in den Stream und man kann es manuell decodieren. Allerdings kann man private sowieso mittels Reflection oder einfach durch Source-Code oder gar Byte-Code-Änderungen austricksen.
  • Die Objektidentität geht bei der Serialisierung verloren. Man kann so leicht verschiedene Kopien von einem Objekt erzeugen. Das muss man bewusst behandeln.
  • Manchmal muss man doch manuellen Serialisierungs-Code schreiben, etwa für Singletons. Das vergisst man bei dem „alles automatisch“-Ansatz leicht, aber wenn man die Serialisierung selber schreibt, vergisst man auch, dass die schöne Copy-Paste-Orgie an genau dieser Stelle einmal eine Pause machen muss
  • Die eingebaute Serialisierung ist langsam
  • Es ist recht mühsam, aber durchaus möglich, ein Objekt in eine Byte-Sequenz zu serialisieren.
  • Das Format ist binär und nicht sehr gut lesbar. Schöner wäre es, wenn man den Serialisierungs-Mechanismus wählen könnte, z.B. JSON, XML, verschiedene Binärformate…
  • Die Serialisierung verleitet dazu, Kommunikation über dieses Binärformat zu machen und damit eine sehr enge Kopplung zu erzwingen
  • Die Serialisierung verleitet dazu, sie für Persistenz zu benutzen und damit wohldurchdachte, ausgereifte und eventuell sogar transaktionale Datenbanken zu unterlaufen. Ganz schlimm: Second Level Cache in Hibernate…

Es hatte sicher manchmal Vorteile, diese Serialisierung zur Verfügung zu haben und für viele Zwecke eignet sie sich auch halbwegs gut. Aber man sollte das ganze doch je nach Anwendungsfall kritisch hinterfragen und gegebenenfalls andere Ansätze in Betracht ziehen oder zumindest den Schwächen Rechnung tragen. Generell wird die native Java-Serialisierung heute als einer der schwerwiegendsten Fehler in der Entwicklungsgeschichte von Java verstanden und man hat sie leider noch in anderen JVM-Sprachen geerbt. Es gibt heute viele Wege, „Serialisierung“ besser zu machen.

Für die serialVersionUID gibt es verschiedene Ansätze. Man kann eine statische Java-Methode schreiben, die so etwas wie „$Id$“ aus svn auswertet und daraus einen numerischen Wert gewinnt. So sind zwei verschiedene Dateien immer mit verschiedener serialVersionUID versehen, wenn sie durch das Sourcecode-Management-System gelaufen sind. Mit git ist es etwas schwieriger, aber man kann das auch umsetzen. Ich gehe hier nicht näher darauf ein, da man die Java-Serialisierung heute ohnehin skeptisch sieht.

Ansonsten ist es vernünftig, serialVersionUID normalerweise leer zu lassen, wenn man damit nicht gezielt arbeiten will und es sich entsprechend in die Checkliste aufnimmt. So ein Feld serialVersionUID zu pflegen ist eigentlich krank und sollte nur im Ausnahmefall mit guten Gründen und einem guten Konzept gemacht werden. Wer trotzdem noch mag oder von den Projektvorgaben gezwungen wird, kann serialVersionUID zufällig erzeugen, z.B. mit dem folgenden Perl-Script:

#!/usr/bin/perl
use bigint;
use Math::Random::Secure qw(irand);
my $r = (irand() << 32) + irand();
printf "%20d\n", $r;

Das ist immer noch etwas besser, als wenn der einmal von der IDE generierte Wert oder die 1 oder 0, mit der man angefangen hat, ewig gleich dort stehen bleibt. Aber man muss den Wert jedes Mal ändern, wenn es größere Änderungen gibt, die das Serialisierungsformat beeinflussen.

Share Button

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*