1. Einführung
In Java funktioniert die Serialisierung nur mit den Objekten, die dies explizit erlauben. Dazu muss die Klasse das spezielle Interface – java.io.Serializable – implementieren.
import java.io.Serializable;
public class Person implements Serializable {
// Felder, Konstruktoren, Methoden
}
Serializable ist ein Marker-Interface: Es hat keine Methoden; es signalisiert der JVM – „diese Klasse darf serialisiert werden, keine Sorge!“. Wenn Sie versuchen, ein Objekt einer Klasse zu serialisieren, die Serializable nicht implementiert, erhalten Sie eine NotSerializableException. Selbst wenn nur ein einziges Feld (oder ein verschachteltes Objekt) nicht serialisierbar ist – die Serialisierung funktioniert nicht.
ObjectOutputStream und ObjectInputStream
- ObjectOutputStream – eine Klasse, die Objekte in einen Stream schreibt (z. B. in eine Datei oder über das Netzwerk).
- ObjectInputStream – eine Klasse, die Objekte aus einem Stream liest.
Sie arbeiten im Paar: Der eine serialisiert das Objekt, der andere – deserialisiert es.
Zentrale Methoden
- writeObject(Object obj) – serialisiert ein Objekt und schreibt es in den Stream.
- readObject() – liest ein Objekt aus dem Stream, deserialisiert es und gibt es zurück.
Wichtig: Beide Klassen arbeiten auf den gewöhnlichen Ein-/Ausgabe-Streams (OutputStream und InputStream). Am häufigsten werden sie zusammen mit Dateistreams – FileOutputStream und FileInputStream – verwendet, können aber auch mit Netzwerkstreams eingesetzt werden.
2. Beispiel für die Serialisierung
Schreiben wir ein einfaches Beispiel: Wir serialisieren und deserialisieren ein Objekt der Klasse Person.
Schritt 1. Die Klasse definieren
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
// transientes Feld wird nicht serialisiert
private transient String secret;
public Person(String name, int age, String secret) {
this.name = name;
this.age = age;
this.secret = secret;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", secret='" + secret + "'}";
}
}
Schritt 2. Objekt in eine Datei serialisieren
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class SerializeDemo {
public static void main(String[] args) throws Exception {
Person person = new Person("Alice", 30, "likes pizza");
// Stream zum Schreiben in die Datei erstellen
FileOutputStream fileOut = new FileOutputStream("person.bin");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
// Objekt speichern
out.writeObject(person);
// Streams schließen
out.close();
fileOut.close();
System.out.println("Objekt in Datei person.bin serialisiert");
}
}
Schritt 3. Objekt aus einer Datei deserialisieren
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class DeserializeDemo {
public static void main(String[] args) throws Exception {
// Stream zum Lesen aus der Datei öffnen
FileInputStream fileIn = new FileInputStream("person.bin");
ObjectInputStream in = new ObjectInputStream(fileIn);
// Objekt wiederherstellen
Person person = (Person) in.readObject();
in.close();
fileIn.close();
System.out.println("Objekt deserialisiert: " + person);
}
}
Erwartete Ausgabe
Objekt in Datei person.bin serialisiert
Objekt deserialisiert: Person{name='Alice', age=30, secret='null'}
Achtung! Das Feld transient wird nicht serialisiert. Nach der Deserialisierung ist es null. Das ist wichtig für temporäre oder sensible Daten.
3. Einschränkungen und Besonderheiten
Alle Felder müssen serialisierbar sein
Wenn eine Klasse Felder hat, die selbst Serializable nicht implementieren (oder solche Objekte enthalten), endet die Serialisierung mit einem Fehler. Felder vom Typ Thread oder Socket lassen sich zum Beispiel nicht „einfach so“ serialisierbar machen.
Statische und transiente Felder
- Statische Felder (static) werden nicht serialisiert: Sie gehören zur Klasse und nicht zu einer konkreten Instanz.
- Transiente Felder – mit transient markierte Felder werden ausdrücklich von der Serialisierung ausgeschlossen und erhalten nach dem Wiederherstellen Standardwerte (null, 0 usw.).
Ausnahmen
- Der Versuch, ein Objekt zu serialisieren, das Serializable nicht implementiert, führt zu einer NotSerializableException.
- Bei der Deserialisierung sind Fehler möglich: Datei nicht gefunden, Klassendiskrepanz, beschädigte Daten usw.
Klassenversionen
Wenn Sie die Struktur einer Klasse nach der Serialisierung ändern (z. B. Felder hinzufügen/entfernen), kann bei der Deserialisierung eine InvalidClassException auftreten. Zur Versionskontrolle wird das spezielle Feld serialVersionUID verwendet (dazu mehr in einer der nächsten Vorlesungen).
4. Praxis: Serialisierung und Deserialisierung eines Objekts in eine Datei
Angenommen, wir haben die Klasse Person und möchten eine Liste von Personen in eine Datei speichern und wieder einlesen.
Klasse Person (serialisierbar)
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
private transient String secret; // wird nicht serialisiert
public Person(String name, int age, String secret) {
this.name = name;
this.age = age;
this.secret = secret;
}
@Override
public String toString() {
return name + " (" + age + "), secret: " + secret;
}
}
Serialisierung einer Liste von Personen
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class SerializeListDemo {
public static void main(String[] args) throws Exception {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30, "likes pizza"));
people.add(new Person("Bob", 25, "hates broccoli"));
FileOutputStream fileOut = new FileOutputStream("people.bin");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
// Liste speichern
out.writeObject(people);
out.close();
fileOut.close();
System.out.println("Liste der Personen serialisiert.");
}
}
Deserialisierung einer Liste von Personen
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
public class DeserializeListDemo {
public static void main(String[] args) throws Exception {
FileInputStream fileIn = new FileInputStream("people.bin");
ObjectInputStream in = new ObjectInputStream(fileIn);
// Liste wiederherstellen
List<Person> people = (List<Person>) in.readObject();
in.close();
fileIn.close();
for (Person p : people) {
System.out.println(p);
}
}
}
Ergebnis:
Alice (30), secret: null
Bob (25), secret: null
5. Häufige Fehler
Fehler Nr. 1: Klasse implementiert Serializable nicht. Wenn Sie implements Serializable vergessen, erhalten Sie beim Versuch zu serialisieren eine NotSerializableException. Das ist der häufigste und einfachste Fehler.
Fehler Nr. 2: Nicht serialisierbares Feld. Wenn ein Objekt ein Feld enthält, das nicht serialisierbar ist (z. B. Thread, Socket oder ein anderer Typ ohne Serializable), „stürzt“ die Serialisierung ab. Markieren Sie solche Felder als transient oder machen Sie sie serialisierbar.
Fehler Nr. 3: Struktur der Klasse geändert. Wenn das Objekt serialisiert wurde und die Klasse später geändert wird (Felder hinzugefügt/entfernt), kann beim Lesen eine InvalidClassException auftreten. Geben Sie serialVersionUID an, um die Version stabil zu kontrollieren.
Fehler Nr. 4: Versuch, statische Felder zu serialisieren. Felder mit dem Modifikator static werden nicht serialisiert. Nach der Deserialisierung haben sie die Werte, die in der Klasse standardmäßig definiert sind, und nicht die zur Zeit der Serialisierung vorhandenen.
Fehler Nr. 5: Streams nicht geschlossen. Wenn die Streams nach der Arbeit nicht geschlossen werden, kann die Datei beschädigt werden oder es kommt zu Ressourcenlecks. Verwenden Sie try-with-resources oder schließen Sie die Streams explizit.
Fehler Nr. 6: Nicht übereinstimmende Klassen. Wenn die Klasse umbenannt oder in ein anderes Paket verschoben wurde, funktioniert die Deserialisierung nicht – es ist eine exakte Übereinstimmung von Klassenname und Paket der im Stream gespeicherten Klasse erforderlich.
GO TO FULL VERSION