ObjectOutputStream w Javie – zapisz obiekt do pliku

Wprowadzenie do serializacji obiektów

W paradygmacie programowania obiektowego częstym zadaniem jest utrwalanie stanu obiektów, aby móc je później odtworzyć. Java dostarcza mechanizmy umożliwiające zapis obiektów do plików, a jednym z kluczowych narzędzi jest klasa ObjectOutputStream. Ta klasa pozwala na przekształcenie danych obiektu w strumień bajtów, który może być zapisany w pliku lub przesłany przez sieć.

Aby zapisać obiekt do pliku przy użyciu ObjectOutputStream, należy przejść przez kilka etapów:

  1. Stworzenie strumienia wyjściowego pliku za pomocą klasy FileOutputStream.
  2. Utworzenie instancji ObjectOutputStream, która jest powiązana ze strumieniem wyjściowym.
  3. Wykorzystanie metody writeObject() do zapisania obiektu do strumienia wyjściowego.
  4. Zamknięcie otwartego strumienia wyjściowego.

Przykładowy zapis pojedynczego obiektu

Poniżej przedstawiono kod ilustrujący zapis pojedynczego obiektu do pliku:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class ZapisObiektu {
    public static void main(String[] args) {
        try {
            // Inicjalizacja obiektu do zapisu
            Osoba osoba = new Osoba("Jan", "Kowalski");

            // Utworzenie strumienia do zapisu w pliku
            FileOutputStream strumienPliku = new FileOutputStream("dane_osoby.dat");

            // Utworzenie ObjectOutputStream, powiązanego ze strumieniem pliku
            ObjectOutputStream strumienObiektow = new ObjectOutputStream(strumienPliku);

            // Zapis obiektu do strumienia
            strumienObiektow.writeObject(osoba);

            // Zamknięcie strumienia
            strumienObiektow.close();

            System.out.println("Obiekt został pomyślnie zapisany do pliku dane_osoby.dat");

        } catch (IOException blad) {
            blad.printStackTrace();
        }
    }
}
    

Zapisywanie kolekcji obiektów

W przypadku konieczności zapisu wielu obiektów, np. listy, należy skorzystać z pętli. Poniższy fragment kodu demonstruje zapis listy obiektów Osoba do pliku:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;

public class ZapisListyObiektow {
    public static void main(String[] args) {
         try {
            // Tworzenie listy obiektów
            List osoby = new ArrayList<>();
            osoby.add(new Osoba("Jan", "Kowalski"));
            osoby.add(new Osoba("Anna", "Nowak"));

            // Inicjalizacja strumienia plikowego
            FileOutputStream strumienPliku = new FileOutputStream("lista_osob.dat");

            // Inicjalizacja strumienia obiektów
            ObjectOutputStream strumienObiektow = new ObjectOutputStream(strumienPliku);

            // Zapis kolejnych obiektów z listy
            for (Osoba osoba : osoby) {
                strumienObiektow.writeObject(osoba);
            }

             // Zamknięcie strumienia
            strumienObiektow.close();

            System.out.println("Lista obiektów została zapisana do pliku lista_osob.dat");

        } catch (IOException blad) {
            blad.printStackTrace();
        }
    }
}
    

Odczytywanie obiektów z pliku z użyciem ObjectInputStream

Do odczytywania obiektów zapisanych w pliku służy klasa ObjectInputStream. Umożliwia ona odtworzenie stanu obiektu na podstawie danych zapisanych w strumieniu wejściowym.

Proces odczytu składa się z następujących kroków:

  1. Utworzenie strumienia wejściowego pliku przy użyciu klasy FileInputStream.
  2. Utworzenie instancji ObjectInputStream powiązanej ze strumieniem wejściowym.
  3. Wykorzystanie metody readObject() w celu odczytania obiektu ze strumienia.
  4. Zamknięcie strumienia wejściowego.

Odczyt pojedynczego obiektu z pliku

Poniższy przykład demonstruje, jak odczytać pojedynczy obiekt z pliku:

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

public class OdczytObiektu {
    public static void main(String[] args) {
        try {
            // Utworzenie strumienia wejściowego
            FileInputStream strumienPliku = new FileInputStream("dane_osoby.dat");
            
            // Utworzenie ObjectInputStream
            ObjectInputStream strumienObiektow = new ObjectInputStream(strumienPliku);

            // Odczytanie obiektu
            Osoba osoba = (Osoba) strumienObiektow.readObject();

            // Zamknięcie strumienia
            strumienObiektow.close();

            System.out.println("Obiekt odczytano z pliku dane_osoby.dat: " + osoba);

        } catch (IOException | ClassNotFoundException blad) {
            blad.printStackTrace();
        }
    }
}
    

Odczyt wielu obiektów z pliku

W przypadku zapisu listy obiektów, odczyt wymaga użycia pętli oraz obsługi wyjątku EOFException, sygnalizującego koniec strumienia. Poniższy kod ilustruje odczyt listy obiektów Osoba z pliku:

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.EOFException;
import java.util.List;
import java.util.ArrayList;

public class OdczytListyObiektow {
    public static void main(String[] args) {
        try {
           // Utworzenie strumienia wejściowego
            FileInputStream strumienPliku = new FileInputStream("lista_osob.dat");
            
            // Utworzenie ObjectInputStream
            ObjectInputStream strumienObiektow = new ObjectInputStream(strumienPliku);

            // Odczyt i dodanie obiektów do listy
            List osoby = new ArrayList<>();
            while (true) {
                try {
                    osoby.add((Osoba) strumienObiektow.readObject());
                } catch (EOFException blad) {
                    break;
                }
            }
            
            // Zamknięcie strumienia
           strumienObiektow.close();

           System.out.println("Obiekty odczytano z pliku lista_osob.dat: " + osoby);
           
        } catch (IOException | ClassNotFoundException blad) {
           blad.printStackTrace();
        }
    }
}
    

Podsumowanie

Użycie klas ObjectOutputStream i ObjectInputStream w Javie stanowi efektywny sposób na zapis i odczyt obiektów z plików. Dzięki temu można łatwo serializować obiekty i zachowywać ich stan, co jest przydatne w wielu aplikacjach, takich jak zapisywanie danych konfiguracyjnych, komunikacja między procesami czy trwałe przechowywanie danych.

Często zadawane pytania (FAQ)

  1. Czym jest serializacja obiektów?
    Serializacja to proces przekształcenia obiektu w strumień bajtów, który może być zapisany lub przesłany. Umożliwia ona przechowywanie stanu obiektu poza czasem życia programu.
  2. Jakie korzyści wynikają z użycia ObjectOutputStream i ObjectInputStream?
    Te klasy upraszczają proces serializacji, oferując intuicyjne metody zapisu i odczytu obiektów. Obsługują polimorfizm, pozwalając na serializację obiektów różnych klas.
  3. Co to jest strumień w kontekście Javy?
    Strumień to uporządkowany przepływ danych, który umożliwia odczyt i zapis informacji. W Javie strumienie reprezentują sekwencję bajtów.
  4. Jaka jest różnica między strumieniem wejściowym a wyjściowym?
    Strumień wejściowy służy do pobierania danych (odczytu), natomiast strumień wyjściowy jest przeznaczony do wysyłania danych (zapisu).
  5. Jakie są alternatywy dla ObjectOutputStream i ObjectInputStream?
    Alternatywami mogą być ObjectWriter i ObjectReader, a także formaty takie jak JSON (z JsonWriter i JsonReader) lub XML, w zależności od potrzeb projektu.
  6. Czy serializacja przy użyciu ObjectOutputStream i ObjectInputStream jest bezpieczna?
    Istnieje ryzyko związane z deserializacją niezaufanych danych, co może prowadzić do problemów z bezpieczeństwem. Należy zachować ostrożność, deserializując obiekty z niepewnych źródeł.
  7. Jak sprawdzić, czy obiekt można serializować?
    Obiekt jest serializowalny, jeśli implementuje interfejs java.io.Serializable.
  8. Czy można zapisać pole (atrybut) obiektu bezpośrednio do pliku?
    Tak, można zapisać pole obiektu, o ile obiekt, do którego to pole należy, jest serializowalny i zostanie zapisany z użyciem ObjectOutputStream.