© Excellent backgrounds/Shutterstock.com
Kolumne: Java-Trickkiste

Kolumne: Java-Trickkiste


Serialisierung ist ein Mechanismus, bei dem Objekte in eine Folge von Bytes verwandelt und umgekehrt daraus wieder Objekte erzeugt werden. Man braucht solche Mechanismen bpsw. für das Aufrufen über ein Netzwerk oder um Objekte in einer Datenbank zu speichern. Java bringt dafür von Haus aus einen Mechanismus mit: die Serialisierung im engeren Sinne. Die ist trotz ihrer Schwächen so weit verbreitet, dass wir sie heute näher betrachten.

Grundlagen zur Serialisierung

Die größte Stärke der Java-Serialisierung ist, dass sie sehr einfach zu verwenden ist. Wenn eine beliebige Klasse das Interface java.io.Serializable implementiert, kann Java deren Instanzen serialisieren (Listing 1). Dabei muss man kein besonderes Programmiermodell beachten – es funktioniert z. B. auch mit final-Feldern und ohne Default-Konstruktor, weil die Deserialisierung Objekte ohne Konstruktoraufruf erzeugt und anschließend die Felder über Reflection setzt. Und Serializable ist ein reines Marker-Interface ohne Methoden, die man implementieren müsste – viel einfacher geht es nicht.

Listing 1

public class Person implements Serializable { private final String name; public Person (String name) { this.name = name; } public String getName () { return name; } }

Zum eigentlichen Serialisieren gibt es die Klasse java.io.ObjectOutputStream:

Person p = newrno"); try (FileOutputStream fos = new FileOutputStream ("dummy.ser"); ObjectOutputStream oos = new ObjectOutputStream (fos)) { oos.writeObject (p); } 

Der Code erzeugt eine Person-Instanz und schreibt sie in die Datei dummy.ser. Dazu erzeugt er mit der try-with-resource-Syntax, die Java seit Version 1.7 unterstützt [1], einen FileOutputStream und einen ObjectOutputStream als Wrapper um diesen herum.

Dann ruft er die Methode writeObject() auf dem ObjectOutputStream mit der Person-Instanz als Argument auf. Die wandelt das Objekt intern in eine Bytefolge um und gibt diese an den FileOutputStream weiter – fertig ist die Serialisierung, das Objekt steht in der Datei. Und weil ObjectOutputStream als Wrapper um einen beliebigen anderen OutputStream funktioniert, kann man Objekte genauso leicht z. B. über eine Netzwerkverbindung (mit Socket.getOutputStream()) oder in ein Bytearray (mit ByteArrayOutputStream) übertragen.

Das Einlesen von Objekten funktioniert analog zum Schreiben, indem man einen ObjectInputStream um einen beliebigen anderen InputStream wickelt:

try (FileInputStream fileInputStream ("dummy.ser"); ObjectInputStream ois = new ObjectInputStream (fis)) { final Person p2 = (Person) ois.readObject (); assert (p2.getName ().equals ("Arno")); }

Die Methode readObject() holt dabei so lange Bytes aus der Datei, bis sie daraus ein fertiges Objekt erzeugen kann, und liefert das zurück – der Code muss natürlich noch auf Person casten.

Durch Hintereinanderschalten von Serialisierung und Deserialisierung kann man recht einfach eine generische Methode zum tiefen Kopieren von Objekten schreiben (Listing 2). Die ist zwar nicht besonders effizient, erfordert dafür aber praktisch keine Kooperation der zu klonenden Objekte. Sie kann nützlich sein, um in Tests verteilte Aufrufe zu mocken, wo Änderungen an Objekten im Server nicht unmittelbar auf Objekte im Client wirken.

Listing 2

static <T> T genericClone (T o) throws IOException, ClassNotFoundExcep...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang