CodeGym /Kurse /JAVA 25 SELF /ObjectOutputStream, ObjectInputStream: Arbeiten mit Strea...

ObjectOutputStream, ObjectInputStream: Arbeiten mit Streams

JAVA 25 SELF
Level 42 , Lektion 3
Verfügbar

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.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION