Szerializalas jegyzet

A VIK Wikiből
Ugrás a navigációhoz Ugrás a kereséshez

Jelen jegyzet, leírás, segédlet nevezzük bárhogy megpróbálja az Objektumorientált Szoftvertervezés és Szoftvertechnológia tárgyakban előszeretettel kérdezett Java szerializálás témaköréből egy áttekinthető, konyhanyelven készült leírást szolgáltatni, sok-sok példával.

Alapok, amiket jó ha tudunk

  • Alapvetően minden nem statikus és nem tranzies attribútuma egy osztálynak szerializálódik, amely megvalósítja a Serializable interfészt.
  • A láthatóság nem befolyásolja a szerializálhatóságot, mind a public, protected és private adattagok szerializálódnak.
  • Kiíráskor minden objektum egyszer íródok ki "rendesen", azt követően az adott objektum újboli kiírásakor - feltételezve, hogy a streamet nem zárták be - csupán egy referencia kerül kiírásra, amely referál az először kiírt "rendes" objektumra.
  • A szerializálhatóság mint tulajdonság, fennmarad az örökléskor is, tehát ha egy osztály sorosítható akkor annak leszármazottai is kimenthetőek lesznek.
  • A szerializálás tiltására van lehetőségünk, a wirteObject metódust kell felüldefiniálnunk abban az osztályban, amelyiket nem szeretnénk szerializálni:
  private void writeObject(ObjectOutputStream o) throws NotSerializableException{
     throw new NotSerializableException("No-no! No Mr. Serializaton!");
  }


  • Két nagyon fontos metódus játszik fő szerepet a szerializálásban:
    • private void writeObject (ObjectOutputStream out) throws IOException
      • A metódus gyakorlatilag teljes kontrollt biztosít a szerializálandó objektum sorosítása felett. A legtöbb esetben egy out.defaultWriteObject() metódushívással kezdődik ezen metódusok implementálása, majd ezt követően lehetőségünk van egyéb adatok sorosítására az erre alkalmas metódusok meghívásával mint pl. az out.writeDouble.
    • private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
      • A writeObject metódus ellentéte, a korábban szerializált obejktumokat ezzel a metódussal tudjuk beolvasni. Felüldefiniálásakor az implementáció itt is - a writeObject-hez hasonlóan - az automatikusan szerializált adatok automatikus visszaolvasásával kezdődik, ami a in.defaultReadObject() meghívásával történik. Ezek után beolvassuk azon extra adatokat amelyeket korábban a writeObject() metódussal szerializáltunk. Fontos, hogy a típusegyezésre nekünk kell figyelnünk, így beolvasáskor helyesen kell kasztolnunk, illetve a primitív típusok esetén a megfelelő beolvasó metódust kell meghívnunk.


  • Egy, két nyalánkság:
    • private Object writeReplace() throws ObjectStreamException
      • A metódus segítségével létrehozható egy "helyettesítő" objektum. A szerializálás során ez az újonnan létrehozott objektum fog szerializálódni az eredeti helyett.
      • Az ObjectOuputStream ellenőrzi, hogy a sorosítani kívánt osztály definiál-e writeReplace() metódust, és ha igen meghívja azt először majd az így visszaadott objektumot fogja végül sorosítani.
    • private Object readResolve() throws ObjectStreamException
      • A writeReplace() ellentéte. Ha a metódus definiált az osztályban, akkor az objektum beolvasása előtt, ennek a metódusnak az eredményét "olvassa be" az ObjectInputStream, a korábban szerializált helyett.
    • private static final ObjectStreamField[] serialPersistentFields
      • Fontos, hogy ez a korábbiakkal ellentétben nem metódus, hanem egy statikus, nem módosítható tömb az osztályban.
      • A tömb használatával lehetőségünk van explicit megadni, hogy az osztály mely attribútumai szerializálódjanak. Fontos, hogy csak a tömbben megjelölt tagváltozók mentődnek ki, tehát hiába van az osztálynak több nem statikus és nem tranziens tagváltozója, csak és kizárólag azon attribútumok fognak szerializálódni amelyek ebben a tömbben szerepelnek!
      • A lenti példában beállítottuk, hogy a Dog osztály szerializálásakor csak az age és a name attribútumok mentődjenek le, a többi adat ne. Fontos, hogy a tranziensnek jelölt attribútumok is szerializálhatók így!
  public class Dog implements Serializable {
     public String name;
     public int age;
     private int ID;
     private String owner;
     
     private static final ObjectStreamField[] serialPersistentFields = { 
           new ObjectStreamField("age",Integer.TYPE), 
           new ObjectStreamField("name", String.TYPE) };
      
     ...
  }

Korábbi vizsgapéldák

OOT - 2015. június 2.

  public class A implements Serializable {
     private int x = 1;
     public transient int y = 2;
     
     private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("y", Integer.TYPE) };
     
     public A() { x = 3; y = 4; }
     
     public Object writeReplace() throws ObjectStreamException {
           A a = new A(); a.x = 5; a.y = 6; return a;
     }
  }
  • Eredmény: x = 0, y = 6

OOT - 2015. június 9.

  public class A implements Serializable {
     
     private transient int x = 8;
     public int y = 4;
     
     public A () { x = 0; y = 4; }
     
     private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("x", Integer.TYPE) };
     
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
        in.defaultReadObject();
        x = y + 4;
     }
  }