Personal tools

PO Serializacja - ćwiczenia

From Studia Informatyczne

<<< Powrót do przedmiotu Programowanie obiektowe

<<< Powrót do wykładu Serializacja

Spis treści

Zadanie 1

Rozszerz klasę Osoba, aby posiadała dwa adresy – adres zameldowania i adres zamieszkania. W klasie SerializacjTest ustaw Zygmuntowi Kotkowi oba rodzaje adresów na alternatywy4. Sprawdź, czy po deserializacji oba adresy są nadal jednym obiektem.

Rozwiązanie

import java.io.*;

class Osoba implements Serializable {
  String nazwisko;
  String imię;
  Adres adresZameldowania, adresZamieszkania;
    
  Osoba(String nazwisko, String imię, Adres adresZameldowania, Adres adresZamieszkania) {
    this.nazwisko = nazwisko;
    this.imię = imię;
    this.adresZameldowania = adresZameldowania;
    this.adresZamieszkania = adresZameldowania;
    System.out.println("wywołanie konstruktora klasy Osoba");   
  }
    
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć+"(" + nazwisko + ", " +
                           imię + ", " +
                           adresZameldowania + ", " +
                           adresZamieszkania + ")";
  }
}

class Adres implements Serializable {
  String miasto;
  String ulica;
  String nrDomu;
  String nrLokalu;
  
  Adres(String miasto, String ulica, String nrDomu, String nrLokalu) {
    this.miasto = miasto;
    this.ulica = ulica;
    this.nrDomu = nrDomu;
    this.nrLokalu = nrLokalu;
    System.out.println("wywołanie konstruktora klasy Adres");   
  }
  
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć + "(" + miasto + ", " +
                             ulica + ", " +
                             nrDomu + ", " +
                             nrLokalu + ")";
  }
}

public class Zad1 {
  public static void main(String[] args) throws Exception {
    Adres alternatywy4 = new Adres("Warszawa",  "Alternatywy", "4", "9");
    Osoba kotek = new Osoba("Kotek", "Zygmunt", alternatywy4, alternatywy4);
    System.out.println(kotek);
    // wersja dla Linuxa
    String nazwaPliku = "/tmp/lista.ser";
    // wersja dla Windows
    //String nazwaPliku = "c:\\lista.ser";
    ObjectOutputStream out = new ObjectOutputStream(
                               new BufferedOutputStream(
                                 new FileOutputStream(nazwaPliku)));
    out.writeObject("Lista lokatorów");
    out.writeObject(kotek);
    out.close();
    
    ObjectInputStream in = new ObjectInputStream(
                             new BufferedInputStream(
                               new FileInputStream(nazwaPliku)));
    String nagłówek = (String) in.readObject();
    kotek = (Osoba) in.readObject();
    in.close();
    System.out.println(kotek);
  } 
}
wywołanie konstruktora klasy Adres
wywołanie konstruktora klasy Osoba
Osoba@ad3ba4(Kotek, Zygmunt, Adres@126b249(Warszawa, Alternatywy, 4, 9), Adres@126b249(Warszawa, Alternatywy, 4, 9))
Osoba@bf2d5e(Kotek, Zygmunt, Adres@13bad12(Warszawa, Alternatywy, 4, 9), Adres@13bad12(Warszawa, Alternatywy, 4, 9))

Zadanie 2

Używając podanej poniżej klasy ElementListy przygotuj trzyelementowy cykl i zserializuj go do pliku, a następnie zdeserializuj. Sprawdź, czy rzeczywiście, jeżeli przed serializacją jakiś obiekt był przypisany na kilka różnych referencji, to po deserializacji nadal będzie na te referencje przypisany jeden obiekt.

class ElementListy implements Serializable {
  String wartość;
  ElementListy następny, poprzedni;
  
  ElementListy(String wartość) {
    this.wartość = wartość;
    System.out.println("wywołanie konstruktora klasy ElementListy");
  }
  
  protected String memAddress() {
    String adrPamięć = super.toString();
    return adrPamięć.substring(adrPamięć.indexOf('@'));
  }
  
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć+"(" + wartość + ", " +
                           następny.memAddress() + ", " +
                           poprzedni.memAddress() + ")";
  }

  public ElementListy getNastępny() {
    return następny;
  }

  public ElementListy getPoprzedni() {
    return poprzedni;
  }

  public void setPoprzedni(ElementListy poprzedni) {
    this.poprzedni = poprzedni;
  }
  
  public void setNastępny(ElementListy następny) {
    this.następny = następny;
  }
}

Rozwiązanie

import java.io.*;

public class Zad2 {
  public static void main(String[] args) throws Exception {
    ElementListy el1, el2, el3;
    el1 = new ElementListy("el1");
    el2 = new ElementListy("el2");
    el3 = new ElementListy("el3");
    el1.setNastępny(el2);
    el1.setPoprzedni(el3);
    el2.setNastępny(el3);
    el2.setPoprzedni(el1);
    el3.setNastępny(el1);
    el3.setPoprzedni(el2);
    
    System.out.println("Przed serializacją");
    System.out.println(el1);
    System.out.println(el2);
    System.out.println(el3);
    // wersja dla Linuxa
    String nazwaPliku = "/tmp/cykl.ser";
    // wersja dla Windows
    //String nazwaPliku = "c:\\cykl.ser";
    ObjectOutputStream out = new ObjectOutputStream(
                               new BufferedOutputStream(
                                 new FileOutputStream(nazwaPliku)));
    out.writeObject(el1);
    out.close();
    
    ObjectInputStream in = new ObjectInputStream(
                             new BufferedInputStream(
                               new FileInputStream(nazwaPliku)));
    ElementListy odczytany = (ElementListy) in.readObject();
    in.close();
    System.out.println("Po deserializacji");
    System.out.println(odczytany);
    System.out.println(odczytany.getNastępny());
    System.out.println(odczytany.getNastępny().getNastępny());
  } 
}
wywołanie konstruktora klasy ElementListy
wywołanie konstruktora klasy ElementListy
wywołanie konstruktora klasy ElementListy
Przed serializacją
zad2.ElementListy@1372a1a(el1, @ad3ba4, @126b249)
zad2.ElementListy@ad3ba4(el2, @126b249, @1372a1a)
zad2.ElementListy@126b249(el3, @1372a1a, @ad3ba4)
Po deserializacji
zad2.ElementListy@10b4b2f(el1, @750159, @1abab88)
zad2.ElementListy@750159(el2, @1abab88, @10b4b2f)
zad2.ElementListy@1abab88(el3, @10b4b2f, @750159)

Zadanie 3

Przygotuj klasę GłębokaKopia, której statyczna metoda kopiuj(Object) będzie wykonywała w pamięci głęboką kopię (całego grafu) obiektu przekazanego jej jako parametr i zwracała ją jako wynik. Użyj napisanej klasy do wykonania głębokiej kopi cyklu el1 używanego w rozwiązaniu poprzedniego zadania.

Rozwiązanie

class GłębokaKopia implements Serializable {
  static public Object kopiuj(Object obj) throws IOException, ClassNotFoundException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    oos.close();
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    Object o = ois.readObject();
    ois.close();
    return o;
  }
}

public class Zad3 {
  public static void main(String[] args) throws Exception {
    ElementListy el1, el2, el3;
    el1 = new ElementListy("el1");
    el2 = new ElementListy("el2");
    el3 = new ElementListy("el3");
    el1.setNastępny(el2);
    el1.setPoprzedni(el3);
    el2.setNastępny(el3);
    el2.setPoprzedni(el1);
    el3.setNastępny(el1);
    el3.setPoprzedni(el2);
    
    System.out.println("Przed kopią");
    System.out.println(el1);
    System.out.println("Po kopii");
    System.out.println((ElementListy) GłębokaKopia.kopiuj(el1));
  } 
}
wywołanie konstruktora klasy ElementListy
wywołanie konstruktora klasy ElementListy
wywołanie konstruktora klasy ElementListy
Przed kopią
zad3.ElementListy@1372a1a(el1, @ad3ba4, @126b249)
Po kopii
zad3.ElementListy@1abab88(el1, @18a7efd, @1971afc)

Zadanie 4

W metodzie main() klasy TestSerializacji po zserializowaniu obiektu kotek, ale przed zserializowaniem obiektu kołek zmień wartość adresu alternatywy4. Sprawdź, czy zmiana jest widoczna po desrializacji. Ten sam eksperyment wykonaj resetując pamięć podręczną strumienia ObjectOutputStream pomiędzy zapisem kolejnych grafów.

Rozwiązanie przed zresetowaniem pamięci podręcznej

import java.io.*;

class Osoba implements Serializable {
  String nazwisko;
  String imię;
  Adres adresZameldowania;
    
  Osoba(String nazwisko, String imię, Adres adresZameldowania) {
    this.nazwisko = nazwisko;
    this.imię = imię;
    this.adresZameldowania = adresZameldowania;
    System.out.println("wywołanie konstruktora klasy Osoba");
  }
    
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć+"(" + nazwisko + ", " +
                           imię + ", " +
                           adresZameldowania + ")";
  }
}

class Adres implements Serializable {
  String miasto;
  String ulica;
  String nrDomu;
  String nrLokalu;   
  
  Adres(String miasto, String ulica, String nrDomu, String nrLokalu) {
    this.miasto = miasto;
    this.ulica = ulica;
    this.nrDomu = nrDomu;
    this.nrLokalu = nrLokalu;
    System.out.println("wywołanie konstruktora klasy Adres");
  }
  
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć + "(" + miasto + ", " +
                             ulica + ", " +
                             nrDomu + ", " +
                             nrLokalu + ")";
  }
}

public class TestSerializacji {
  public static void main(String[] args) throws Exception {
    Adres alternatywy4 = new Adres("Warszawa",  "Alternatywy", "4", "9");
    Osoba kotek = new Osoba("Kotek", "Zygmunt", alternatywy4);
    Osoba kołek= new Osoba("Kołek", "Zdzisław", alternatywy4);
    System.out.println(kotek);
    System.out.println(kołek);
    // wersja dla Linuxa
    String nazwaPliku = "/tmp/lista.ser";
    // wersja dla Windows
    //String nazwaPliku = "c:\\lista.ser";
    ObjectOutputStream out = new ObjectOutputStream(
                               new BufferedOutputStream(
                                 new FileOutputStream(nazwaPliku)));
    out.writeObject("Lista lokatorów");
    out.writeObject(kotek);
    // Wspólne obiekty mają wartość z chwili, gdy pierwszy raz wystąpią w serializowanym grafie.
    alternatywy4.nrLokalu = "10";
    out.writeObject(kołek);    
    out.close();

    ObjectInputStream in = new ObjectInputStream(
                             new BufferedInputStream(
                               new FileInputStream(nazwaPliku)));
    String nagłówek = (String) in.readObject();
    kotek = (Osoba) in.readObject();
    kołek = (Osoba) in.readObject();
    in.close();
    System.out.println(kotek);
    System.out.println(kołek);
  } 
}

wywołanie konstruktora klasy Adres
wywołanie konstruktora klasy Osoba
wywołanie konstruktora klasy Osoba
Osoba@1372a1a(Kotek, Zygmunt, Adres@ad3ba4(Warszawa, Alternatywy, 4, 9))
Osoba@126b249(Kołek, Zdzisław, Adres@ad3ba4(Warszawa, Alternatywy, 4, 9))
Osoba@1372a1a(Kotek, Zygmunt, Adres@ad3ba4(Warszawa, Alternatywy, 4, 9))
Osoba@126b249(Kołek, Zdzisław, Adres@ad3ba4(Warszawa, Alternatywy, 4, 9))

Rozwiązanie po zresetowaniu pamięci podręcznej

Po zresetowaniu pamięci podręcznej po zdeserializowaniu grafy nie będą dzieliły żadnych obiektów.

import java.io.*;

class Osoba implements Serializable {
  String nazwisko;
  String imię;
  Adres adresZameldowania;
    
  Osoba(String nazwisko, String imię, Adres adresZameldowania) {
    this.nazwisko = nazwisko;
    this.imię = imię;
    this.adresZameldowania = adresZameldowania;
    System.out.println("wywołanie konstruktora klasy Osoba");
  }
    
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć+"(" + nazwisko + ", " +
                           imię + ", " +
                           adresZameldowania + ")";
  }
}

class Adres implements Serializable {
  String miasto;
  String ulica;
  String nrDomu;
  String nrLokalu;   
  
  Adres(String miasto, String ulica, String nrDomu, String nrLokalu) {
    this.miasto = miasto;
    this.ulica = ulica;
    this.nrDomu = nrDomu;
    this.nrLokalu = nrLokalu;
    System.out.println("wywołanie konstruktora klasy Adres");
  }
  
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć + "(" + miasto + ", " +
                             ulica + ", " +
                             nrDomu + ", " +
                             nrLokalu + ")";
  }
}

public class TestSerializacji {
  public static void main(String[] args) throws Exception {
    Adres alternatywy4 = new Adres("Warszawa",  "Alternatywy", "4", "9");
    Osoba kotek = new Osoba("Kotek", "Zygmunt", alternatywy4);
    Osoba kołek= new Osoba("Kołek", "Zdzisław", alternatywy4);
    System.out.println(kotek);
    System.out.println(kołek);
    // wersja dla Linuxa
    String nazwaPliku = "/tmp/lista.ser";
    // wersja dla Windows
    //String nazwaPliku = "c:\\lista.ser";
    ObjectOutputStream out = new ObjectOutputStream(
                               new BufferedOutputStream(
                                 new FileOutputStream(nazwaPliku)));
    out.writeObject("Lista lokatorów");
    out.writeObject(kotek);
    // Wspólne obiekty mają wartość z chwili, gdy pierwszy raz wystąpią w serializowanym grafie.
    alternatywy4.nrLokalu = "10";
    // Po zresetowaniu pamięci podręcznej po zdeserializowaniu grafy nie będą dzieliły żadnych obiektów.
    out.reset();
    out.writeObject(kołek);    
    out.close();

    ObjectInputStream in = new ObjectInputStream(
                             new BufferedInputStream(
                               new FileInputStream(nazwaPliku)));
    String nagłówek = (String) in.readObject();
    kotek = (Osoba) in.readObject();
    kołek = (Osoba) in.readObject();
    in.close();
    System.out.println(kotek);
    System.out.println(kołek);
  } 
}
wywołanie konstruktora klasy Adres
wywołanie konstruktora klasy Osoba
wywołanie konstruktora klasy Osoba
Osoba@1372a1a(Kotek, Zygmunt, Adres@ad3ba4(Warszawa, Alternatywy, 4, 9))
Osoba@126b249(Kołek, Zdzisław, Adres@ad3ba4(Warszawa, Alternatywy, 4, 9))
Osoba@1632c2d(Kotek, Zygmunt, Adres@1e97676(Warszawa, Alternatywy, 4, 9))
Osoba@60420f(Kołek, Zdzisław, Adres@19106c7(Warszawa, Alternatywy, 4, 10))

Zadanie 5

Wymyśl sposób na zabronienie wykonywania serializacji na danej klasie. Niech każda próba serializacji zgłasza wyjątek. Podaj przykład tak zabezpieczonej klasy oraz spróbuj ją zserializować.

Wskazówka

Zaimplementuj metody writeObject() i readObject().

Rozwiązanie

import java.io.*;

class Nieserializowalna implements Serializable {
  private void writeObject(ObjectOutputStream out) throws IOException {
    throw new NotSerializableException("Serializacja niedozwolona!");
  }
  
  private void readObject(ObjectInputStream in) throws IOException {
    throw new NotSerializableException("Serializacja niedozwolona!");
  }
}

public class Nieserializowalna1 {
  public static void main(String[] args) {
    ObjectOutputStream oos = null;
    try {
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      oos = new ObjectOutputStream(bos);
      oos.writeObject(new Nieserializowalna());
    } catch (Exception e) {
      System.out.println(e.getMessage());
    } finally {
      try {
        oos.close();
      } catch (Exception e) {
        System.out.println("Nie udało się zamknąć strumienia");
      }
    }
  } 
}

Zadanie 6

Wymyśl sposób na zabronienie wykonywania serializacji na danej klasie i wszystkich jej podklasach. Podaj przykład tak zabezpieczonej klasy oraz spróbuj ją zserializować.

Wskazówka

Zaimplementuj interfejs Externalizable.

Rozwiązanie

import java.io.*;

class TrwaleNieserializowalna implements Externalizable {
  final public void writeExternal(ObjectOutput out) throws IOException {
    throw new NotSerializableException("Serializacja trwale niedozwolona!");
  }
  
  final public void readExternal(ObjectInput in) throws IOException {
    throw new NotSerializableException("Serializacja trwale niedozwolona!");
  }
}

public class Nieserializowalna2 {
  public static void main(String[] args) {
    ObjectOutputStream oos = null;
    try {
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      oos = new ObjectOutputStream(bos);
      oos.writeObject(new TrwaleNieserializowalna());
    } catch (Exception e) {
      System.out.println(e.getMessage());
    } finally {
      try {
        oos.close();
      } catch (Exception e) {
        System.out.println("Nie udało się zamknąć strumienia");
      }
    }
  } 
}

Zadanie 7

Sprawdź, jak klasa XMLEncoder wypisze cykl el1 występujący w rozwiązaniu zadania 2.

Rozwiązanie klasa ElementListy

public class ElementListy {
  String wartość;
  ElementListy następny, poprzedni;
  
  ElementListy(String wartość) {
    this.wartość = wartość;
    System.out.println("wywołanie konstruktora klasy ElementListy");
  }
  
  public ElementListy() {
    System.out.println("wywołanie bezparametrowego konstruktora klasy ElementListy");
  }
  
  protected String memAddress() {
    String adrPamięć = super.toString();
    return adrPamięć.substring(adrPamięć.indexOf('@'));
  }

  public ElementListy getNastępny() {
    return następny;
  }

  public ElementListy getPoprzedni() {
    return poprzedni;
  }

  public String getWartość() {
    return wartość;
  }

  public void setNastępny(ElementListy następny) {
    this.następny = następny;
  }

  public void setPoprzedni(ElementListy poprzedni) {
    this.poprzedni = poprzedni;
  }

  public void setWartość(String wartość) {
    this.wartość = wartość;
  }
  
  public String toString() {
    String adrPamięć = super.toString();
    return adrPamięć+"(" + wartość + ", " +
                           następny.memAddress() + ", " +
                           poprzedni.memAddress() + ")";
  }
}

Rozwiązanie klasa Zad7

import java.beans.*;
import java.io.*;

public class Zad7 {
  public static void main(String[] args) throws IOException {
    ElementListy el1, el2, el3;
    el1 = new ElementListy("el1");
    el2 = new ElementListy("el2");
    el3 = new ElementListy("el3");
    el1.setNastępny(el2);
    el1.setPoprzedni(el3);
    el2.setNastępny(el3);
    el2.setPoprzedni(el1);
    el3.setNastępny(el1);
    el3.setPoprzedni(el2);
    
    System.out.println("Przed serializacją");
    System.out.println(el1);
    System.out.println(el2);
    System.out.println(el3);
    // wersja dla Linuxa
    //String nazwaPliku = "/tmp/cykl.xml";
    // wersja dla Windows
    String nazwaPliku = "c:\\cykl.xml";
    XMLEncoder out = new XMLEncoder(
                       new BufferedOutputStream(
                         new FileOutputStream(nazwaPliku)));
    out.writeObject(el1);
    out.close();
  }
}

Rozwiązanie wynikowy XML

<?xml version="1.0" encoding="UTF-8"?> 
<java version="1.5.0_06" class="java.beans.XMLDecoder"> 
  <object id="ElementListy0" class="ElementListy"> 
    <void property="następny"> 
      <object id="ElementListy1" class="ElementListy"> 
        <void property="następny"> 
          <object id="ElementListy2" class="ElementListy"> 
            <void property="następny"> 
              <object idref="ElementListy0"/> 
            </void> 
            <void property="poprzedni"> 
              <object idref="ElementListy1"/> 
            </void> 
            <void property="wartość"> 
              <string>el3</string> 
            </void> 
          </object> 
        </void> 
        <void property="poprzedni"> 
          <object idref="ElementListy0"/> 
        </void> 
        <void property="wartość"> 
          <string>el2</string> 
        </void> 
      </object> 
    </void> 
    <void property="poprzedni"> 
      <object idref="ElementListy2"/> 
    </void> 
    <void property="wartość"> 
      <string>el1</string> 
    </void> 
  </object> 
</java>