CodeGym /Kursy /JAVA 25 SELF /ObjectOutputStream, ObjectInputStream: praca ze strumieni...

ObjectOutputStream, ObjectInputStream: praca ze strumieniami

JAVA 25 SELF
Poziom 42 , Lekcja 3
Dostępny

1. Wprowadzenie

W Javie serializacja działa tylko dla tych obiektów, które wyraźnie na to zezwalają. W tym celu klasa musi implementować specjalny interfejs — java.io.Serializable.

import java.io.Serializable;

public class Person implements Serializable {
    // Pola, konstruktory, metody
}

Serializable — to interfejs znacznikowy: nie ma metod, po prostu informuje JVM — „tę klasę można serializować, bez obaw!”. Jeśli spróbujesz zserializować obiekt klasy, która nie implementuje Serializable, otrzymasz wyjątek NotSerializableException. Nawet jeśli choć jedno pole (lub obiekt zagnieżdżony) nie jest serializowalne — serializacja się nie powiedzie.

ObjectOutputStream i ObjectInputStream

  • ObjectOutputStream — klasa, która zapisuje obiekty do strumienia (np. do pliku lub przez sieć).
  • ObjectInputStream — klasa, która odczytuje obiekty ze strumienia.

Działają w parze: jeden serializuje obiekt, drugi — deserializuje.

Podstawowe metody

  • writeObject(Object obj) — serializuje obiekt i zapisuje go do strumienia.
  • readObject() — odczytuje obiekt ze strumienia, deserializuje i zwraca go.

Ważne: oba klasy działają na wierzchu zwykłych strumieni wejścia-wyjścia (OutputStream i InputStream). Najczęściej używa się ich wraz ze strumieniami plikowymi — FileOutputStream i FileInputStream, ale można je stosować także ze strumieniami sieciowymi.

2. Przykład serializacji

Napiszmy prosty przykład: zserializujemy i zdeserializujemy obiekt klasy Person.

Krok 1. Opis klasy

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;
    // pole transient nie będzie serializowane
    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 + "'}";
    }
}

Krok 2. Serializacja obiektu do pliku

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");

        // Tworzymy strumień do zapisu do pliku
        FileOutputStream fileOut = new FileOutputStream("person.bin");
        ObjectOutputStream out = new ObjectOutputStream(fileOut);

        // Zapisujemy obiekt
        out.writeObject(person);

        // Zamykamy strumienie
        out.close();
        fileOut.close();

        System.out.println("Obiekt zserializowano do pliku person.bin");
    }
}

Krok 3. Deserializacja obiektu z pliku

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class DeserializeDemo {
    public static void main(String[] args) throws Exception {
        // Otwieramy strumień do odczytu z pliku
        FileInputStream fileIn = new FileInputStream("person.bin");
        ObjectInputStream in = new ObjectInputStream(fileIn);

        // Odtwarzamy obiekt
        Person person = (Person) in.readObject();

        in.close();
        fileIn.close();

        System.out.println("Obiekt zdeserializowany: " + person);
    }
}

Oczekiwany wynik

Obiekt zserializowano do pliku person.bin
Obiekt zdeserializowany: Person{name='Alice', age=30, secret='null'}

Uwaga! Pole transient nie jest serializowane. Po deserializacji będzie miało wartość null. Jest to ważne dla danych tymczasowych lub wrażliwych.

3. Ograniczenia i szczegóły

Wszystkie pola muszą być serializowalne

Jeśli klasa ma pola, które same nie implementują Serializable (lub zawierają takie obiekty), serializacja zakończy się błędem. Na przykład pól typu Thread lub Socket nie da się tak po prostu uczynić serializowalnymi.

Pola statyczne i transient

  • Pola statyczne (static) nie są serializowane: należą do klasy, a nie do konkretnego obiektu.
  • Pola transient — oznaczone jako transient są jawnie wyłączane z serializacji i po odtworzeniu otrzymują wartości domyślne (null, 0 itd.).

Wyjątki

  • Próba zserializowania obiektu, który nie implementuje Serializable, powoduje NotSerializableException.
  • Podczas deserializacji możliwe są błędy: nie znaleziono pliku, niedopasowanie klas, uszkodzone dane itd.

Wersje klas

Jeśli zmienisz strukturę klasy po serializacji (np. dodasz/usuniesz pola), podczas deserializacji może wystąpić InvalidClassException. Do kontroli wersji używa się specjalnego pola serialVersionUID (więcej o nim w jednej z kolejnych lekcji).

4. Praktyka: serializacja i deserializacja obiektu do pliku

Załóżmy, że mamy klasę Person i chcemy zapisać listę osób do pliku oraz odczytać ją z powrotem.

Klasa Person (serializowalna)

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;
    private transient String secret; // nie będzie serializowane

    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;
    }
}

Serializacja listy osób

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);

        // Zapisujemy listę
        out.writeObject(people);

        out.close();
        fileOut.close();

        System.out.println("Lista osób została zserializowana.");
    }
}

Deserializacja listy osób

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);

        // Odtwarzamy listę
        List<Person> people = (List<Person>) in.readObject();

        in.close();
        fileIn.close();

        for (Person p : people) {
            System.out.println(p);
        }
    }
}

Wynik:

Alice (30), secret: null
Bob (25), secret: null

5. Typowe błędy

Błąd nr 1: Klasa nie implementuje Serializable. Jeśli zapomnisz dodać implements Serializable, przy próbie serializacji otrzymasz NotSerializableException. To najczęstszy i najprostszy błąd.

Błąd nr 2: Nieserializowalne pole. Jeśli obiekt zawiera pole, które nie jest serializowalne (np. Thread, Socket lub inny typ bez Serializable), serializacja się nie powiedzie. Oznacz takie pola jako transient lub spraw, by były serializowalne.

Błąd nr 3: Zmiana struktury klasy. Jeśli obiekt został zserializowany, a później klasa została zmieniona (dodano/usunięto pola), przy odczycie możliwy jest InvalidClassException. Ustal serialVersionUID dla stabilnej kontroli wersji.

Błąd nr 4: Próba serializacji pól statycznych. Pola z modyfikatorem static nie są serializowane. Po deserializacji ich wartości będą takie, jak wynikające z definicji klasy, a nie te, które były podczas serializacji.

Błąd nr 5: Niezamknięte strumienie. Jeśli nie zamykasz strumieni po zakończeniu pracy, możesz otrzymać uszkodzony plik lub wyciek zasobów. Używaj try-with-resources albo zamykaj strumienie jawnie.

Błąd nr 6: Niedopasowanie klas. Jeśli klasa została przemianowana lub przeniesiona do innego pakietu, deserializacja się nie powiedzie — wymagane jest dokładne dopasowanie nazwy i pakietu klasy zapisanej w strumieniu.

1
Zadanie
JAVA 25 SELF, poziom 42, lekcja 3
Niedostępne
Serializacja prostego obiektu
Serializacja prostego obiektu
1
Zadanie
JAVA 25 SELF, poziom 42, lekcja 3
Niedostępne
Pole transient i serializacja
Pole transient i serializacja
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION