Od javy 1.5 został wprowadzony mechanizm serjalizacji czyli bezpośredniego zapisu stanu obiektów do pliku czy też przesyłanie obiektów pomiędzy aplikacjami w sieci.
Aby było to jednak możliwe konieczne jest implementowanie przez klasę interfejsu java.io.serializable. Jest to interfejs znacznikowy co oznacza że interfejs nie zawiera żadnej metody. Jego głównym zadaniem jest wskazanie wirtualnej maszynie javy że egzemplarze klasy które implementują ten interfejs mogą być serjalizowane. W przeciwnym razie przy każdej próbie serializacji zostanie wygenerowany wyjątek „NotSerializableException”.
Jeżeli klasa podlegająca serializacji jest klasą złożoną z innych typów obiektowych to te klasy również muszą implementować interfejs serializable. Zatrzymując się na chwilę w temacie serializacji i dziedziczenia jest tutaj kilka istotnych kwestii. I tak: jeśli rozszerzamy klasę, która nie implementuje interfejsu Serializable, a jej klasa pochodna tak, to serializacji podlegają tylko pola z klasy pochodnej. Jeśli klasa implementuje interfejs Serializable, to możliwość serializacji mają wszystkie klasy pochodne w hierarchii dziedziczenia. Następnie pierwsza klasa w hierarchii dziedziczenia (np Animal), która nie jest serializowalna musi mieć dostępny konstruktor bezparametrowy w celu poprawnej serializacji instancji klasy Dog. Natomiast klasa pochodna (Dog) już takiego konstruktora nie wymaga.
public class Animal{} public class Dog extends Animal implaments Serializable{}
W procesie serializacji istnieje też możliwość pomijania informacji których nie chcemy serializować. Operacja ta jest wykonywana przy wykorzystaniu słówka transient co może wyglądać następująco:
private transient String secondName;
Gdy już nasz klasa implementuje interfejs Serializable możliwość zapisu obiektu występuje za pomocą klas: ObjectOutputStream i FileOutputStream. W tym momencie powinno również nastąpić obsłużenie wyjątku które można wykonac za pomocą bloku try-with-resource, poniewąz dzięki niemu strumień danych zostanie zamknięty. Proszę spójrz na poniższy przykład:
String fileName = "animal.obj";
Dog dog = new Dog("Atos");
Dog dog2 = new Dog("Portos");
try ( ObjectOutputStream oos = new ObjectOutputStream
(new FileOutputStream(fileName)){
oos.writeObject(dog);
oos.writeObject(dog2);
System.out.println("Zapis obiektów do pliku");
} catch {IOException ex) {
System.out.err("Błąd podczas zapisu pliku " + fileName);
ex.printStackTrace();
}
}
}
W przykładzie powyżej zapisujemy dwa obiekty do pliku „animal.obj” z wykorzystaniem metody writeObject. W konstruktorze klasy ObjectOutputStream przekazałam FileOutputStream do którego przekazałam zdefiniowany wcześniej plik. Oczywiście cały zapis można rozbić na osobne linijki i zdefiniowanie obiektu dla klasy FileOutputStream który został by przekazany do konstruktora klasy ObjectOutputStream. Uznałam jednak iż zapis w jednej linijce jest nieco krótszy i nie wpływa negatywnie na czytelność kodu.
Po uruchomieniu takiego programu i odświeżeniu zawartości powinniśmy móc zobaczyć nasz „animal.obj” plik.