CodeGym /Kurse /JAVA 25 SELF /Schnittstelle Serializable: grundlegende Prinzipien

Schnittstelle Serializable: grundlegende Prinzipien

JAVA 25 SELF
Level 42, Lektion 1
Verfügbar

1. Die Schnittstelle Serializable

Erinnern Sie sich an die Beispiele aus der letzten Vorlesung? Java-Klassen, die wir serialisieren möchten, müssen ein spezielles Interface implementieren – java.io.Serializable. Das ist ein sogenanntes Marker-Interface: Es enthält keine Methoden, sondern „markiert“ eine Klasse lediglich als für die Serialisierung geeignet. Implementiert eine Klasse dieses Interface, erlaubt die JVM, ihre Objekte mit den Standardmitteln zu serialisieren.

Es ist nicht ratsam, wahllos alles zu serialisieren, denn nicht alle Objekte kann oder sollte man in Bytes speichern. Einige Objekte hängen vom Zustand des Betriebssystems, offenen Dateien oder Netzwerkverbindungen ab. Daher verlangt Java, dass eine Klasse ausdrücklich als serialisierbar markiert wird.

Ein Marker-Interface ist wie ein Aufkleber „zur Verpackung freigegeben“ auf einer Schachtel. Fehlt dieser Aufkleber – der Verpacker (die JVM) verweigert die Arbeit.

Beispiel: Deklaration einer serialisierbaren Klasse

import java.io.Serializable;

public class User implements Serializable {
    private String name;
    private int age;

    // Konstruktor, Getter und Setter
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Zur Anschauung: Methode toString()
    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}

Achten Sie darauf:

  • Wir haben einfach implements Serializable zur Klassendeklaration hinzugefügt.
  • Es müssen keine Methoden implementiert werden (das Interface ist leer).
  • Alle Standard-Java-Klassen, die serialisierbar sind (z. B. ArrayList, HashMap, String), implementieren Serializable bereits.

2. Wie man eine eigene Klasse serialisierbar macht

Regel Nr. 1: Fügen Sie einfach implements Serializable hinzu

Das ist alles, was für die Klasse selbst erforderlich ist. Es gibt jedoch Feinheiten!

Wichtig: Auch alle referenzierten Objekte müssen serialisierbar sein.
Wenn Ihre Klasse Felder mit Verweisen auf andere Objekte hat, müssen diese ebenfalls serialisierbar sein. Zum Beispiel:

public class Profile implements Serializable {
    private User user; // User muss serialisierbar sein!
    private int level;
}

Ist auch nur eines der Felder nicht serialisierbar, wird beim Versuch der Serialisierung eine Ausnahme ausgelöst.

3. Beispiel für Serialisierung und Deserialisierung

Schauen wir uns an, wie man ein Objekt in eine Datei serialisiert und daraus wieder deserialisiert. Dazu werden die Klassen ObjectOutputStream und ObjectInputStream verwendet.

Beispiel: Serialisierung eines User-Objekts in eine Datei

import java.io.*;

public class SerializeDemo {
    public static void main(String[] args) {
        User user = new User("Alice", 30);

        // Objekt in Datei speichern
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
            oos.writeObject(user);
            System.out.println("Objekt wurde erfolgreich in die Datei user.ser serialisiert");
        } catch (IOException e) {
            System.out.println("Serialisierungsfehler: " + e.getMessage());
        }
    }
}

Was passiert hier?

  • Wir erzeugen ein User-Objekt.
  • Wir öffnen einen ObjectOutputStream, der in die Datei "user.ser" schreibt.
  • Wir rufen writeObject(user) auf. In diesem Moment verwandelt die JVM das Objekt in einen Bytestrom und speichert ihn in die Datei.

Beispiel: Deserialisierung eines Objekts aus einer Datei

import java.io.*;

public class DeserializeDemo {
    public static void main(String[] args) {
        // Objekt aus Datei lesen
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
            User user = (User) ois.readObject();
            System.out.println("Objekt erfolgreich wiederhergestellt: " + user);
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("Deserialisierungsfehler: " + e.getMessage());
        }
    }
}

Was passiert hier?

  • Wir öffnen einen ObjectInputStream, der aus der Datei "user.ser" liest.
  • Wir rufen readObject() auf. Die JVM stellt das Objekt aus den Bytes wieder her.
  • Vergessen Sie nicht, das Ergebnis auf den gewünschten Typ zu casten (User), da readObject() ein Object zurückgibt.
  • Es kann eine ClassNotFoundException auftreten, wenn die Klasse User bei der Deserialisierung nicht gefunden wird.

Alles zusammen: Serialisierung und Deserialisierung

import java.io.*;

public class SerializationExample {
    public static void main(String[] args) {
        User user = new User("Bob", 22);

        // Serialisierung
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
            oos.writeObject(user);
            System.out.println("Serialisierung abgeschlossen!");
        } catch (IOException e) {
            System.out.println("Serialisierungsfehler: " + e.getMessage());
        }

        // Deserialisierung
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
            User loaded = (User) ois.readObject();
            System.out.println("Deserialisierung abgeschlossen! " + loaded);
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("Deserialisierungsfehler: " + e.getMessage());
        }
    }
}

Ergebnis:

Serialisierung abgeschlossen!
Deserialisierung abgeschlossen! User{name='Bob', age=22}

4. Was „unter der Haube“ bei der Serialisierung passiert

Wenn Sie writeObject aufrufen, prüft die JVM zunächst, ob die Klasse das Interface Serializable implementiert. Ist die Klasse nicht als serialisierbar markiert, wird eine Ausnahme ausgelöst. Anschließend geht die JVM alle normalen Felder des Objekts durch (also solche, die nicht static und nicht transient sind) und schreibt deren Werte in den Bytestrom. Treffen dabei weitere Objekte auf, werden diese rekursiv serialisiert – jedoch nur, wenn auch sie Serializable implementieren.

Bei der Deserialisierung wird das Objekt ohne Aufruf des regulären Konstruktors erzeugt, und seine Felder werden mit den gespeicherten Werten befüllt – gewissermaßen ein „Konstruktor ohne Konstruktor“, der das Objekt aus dem Bytestrom wiederbelebt.

Bestimmte Felder werden nicht serialisiert. Statische Felder (static) gehören der Klasse und nicht der einzelnen Instanz; ihre Werte werden daher nicht gespeichert. Felder, die als transient markiert sind, werden ebenfalls ausgelassen – praktisch für temporäre Daten, Caches oder vertrauliche Informationen wie Passwörter.

Ablauf der Serialisierung

flowchart TB
    A[User-Objekt im Speicher] -- writeObject --> B[ObjectOutputStream]
    B -- speichert Bytes --> C[Datei user.ser]
    C -- readObject --> D[ObjectInputStream]
    D -- stellt wieder her --> E[User-Objekt im Speicher]

5. Typische Fehler beim Einsatz von Serializable

Fehler Nr. 1: Feld-Referenz auf ein nicht serialisierbares Objekt. Wenn die Klasse User ein Feld vom Typ, zum Beispiel, Thread oder Socket erhält, funktioniert die Serialisierung nicht. Nicht alle Objekte lassen sich serialisieren – behalten Sie das im Hinterkopf!

Fehler Nr. 2: Nicht serialisierbare innere Klassen. Wenn die Klasse User eine innere Klasse enthält, die nicht static ist, kann die Serialisierung fehlschlagen. Verwenden Sie besser static geschachtelte Klassen oder separate Klassendateien.

Fehler Nr. 3: Versuch, ein static-Feld zu serialisieren. Statische Felder werden nicht serialisiert – sie gehören der Klasse, nicht dem Objekt. Nach der Deserialisierung hat das static-Feld den in der Klasse definierten Wert, nicht den aus dem serialisierten Objekt.

Fehler Nr. 4: Abweichende Klassenversionen. Wenn Sie die Struktur der Klasse nach der Serialisierung ändern (z. B. ein Feld hinzufügen oder entfernen) und anschließend versuchen, ein altes Objekt zu deserialisieren, kann eine InvalidClassException auftreten. Zur Versionskontrolle wird das spezielle Feld serialVersionUID verwendet – darüber sprechen wir in der nächsten Vorlesung ausführlicher.

1
Aufgabe
JAVA 25 SELF, Level 42, Lektion 1
Gesperrt
Marker-Interface Serializable
Marker-Interface Serializable
1
Aufgabe
JAVA 25 SELF, Level 42, Lektion 1
Gesperrt
Einfache Serialisierung und Deserialisierung
Einfache Serialisierung und Deserialisierung
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION