4it101»Adv Soubory

Adv Soubory

Soubory

Před čtením této části doporučuji prostudovat ve skriptech kapitolu věnovanou souborům.

Uložení a obnovení stavu hry

Pro ukládání a načítání stavu hry je nejvhodnější použít serializaci objektů.

Serializace objektů

  • serializace objektů umožňuje převést libovolný objekt (instanci) na posloupnost bajtů, kterou lze později načíst a původní objekt obnovit,
  • serializace je nezávislá na platformě,
  • užití:
    • pro ukládání/obnovování stavu instancí,
    • definování počátečního stavu instance v JavaBeanech,
    • pro přenos pro síti - RMI

Třídy ObjectOutputStream a ObjectInputStream

  • zapisování objektu do souboru (do výstupního streamu):
    • zajišťuje třída ObjectOutputStream(OutputStream os) z java.io, která má metodu
	     void writeObject(Object o)
  • omezení:
    • ukládají se pouze objekty implementující rozhraní Serializable,
    • ukládají se i všechny datové atributy v objektu (celou síť objektů),
    • primitivní datové typy je potřeba zabalit do referenčních,
  • čtení objektu ze streamu (ze souboru):
    • třída ObjectInputStream(InputStream is) z java.io s metodou
	     Object readObject()
  • omezení:
    • nutno zachovat pořadí objektů v jakém se ukládá,
    • musí být k dispozici stejný soubor .class (stejné jméno a verze), který se použil při ukládání,

Rozhraní Serializable

  • rozhraní Serializable je pouze příznak, nemá žádné metody,
  • při implementaci rozhraní Serializable je nutné:
    • všechny datové atributy musí být buď typu, který implementuje rozhraní Serializable, nebo to musí být primitivní datové typy (int, char, double, boolean, ...),
    • předek by měl též implementovat rozhraní Serializable (neplatí pro třídu Object, neboť nemá datové atributy),

Uložení datových atributů do souboru

Následující kód by měl být v odpovídající metodě ve třídě Hra:

   try {
      // otevření soubor pro zápis instancí
      // proměnná soubor buď obsahuje jméno souboru (tj. je typu String)
      // nebo (lépe) je to instance třídy File
      ObjectOutputStream vystup = new ObjectOutputStream(
				new FileOutputStream(soubor));
      // tímto příkazem se zapíše do souboru instance třídy herniPlan
      // a všechny instance na které odkazuje - celý strom instancí
      vystup.writeObject(herniPlan);
      // zapisuji i seznam platných příkazů
      vystup.writeObject(platnePrikazy);
      vystup.close();
   }
   catch (FileNotFoundException e) {
      System.out.println("Nepodarilo se otevrit vystupni soubor \""+soubor+"\"");
      return;
   }
   catch (InvalidClassException e) {
      System.out.println("Chyba ve tride urcene k serializaci");
      return;
   }
   catch (NotSerializableException e) {
      System.out.println("Nektere objekty nejsou serializovane");
      return;
   }
   catch (IOException e) {
      System.out.println("Chyba pri ukladani do souboru");
      return;
   } 

Načtení datových atributů ze souboru:

Následující kód tvoří většinu odpovídající metody ve třídě Hra:

   try {
       // otevření souboru pro čtení
       ObjectInputStream vstup = new ObjectInputStream(
					new FileInputStream(soubor));
       // načtení datových atributů, načítají se ve stejném pořadí, v jakém
       // se zapisovaly,
       herniPlan = (HerniPlan)vstup.readObject();
       platnePrikazy = (SeznamPrikazu)vstup.readObject();
       vstup.close();
   }
   catch (FileNotFoundException e) {
       System.out.println("Nepodařilo se otevřít soubor s uloženými objekty");
       return;
   }
   catch (StreamCorruptedException e) {
       System.out.println("Chybná struktura souboru s objekty");
       return;
   }
   catch (IOException e) {
       System.out.println("Chyba při čtení ze souboru s uloženými objekty");
       return;
   }
   catch (ClassNotFoundException e) {
       System.out.println("V souboru nejsou uloženy instance příslušné třídy ");
       return;
   } 

Postup vytvoření příkazů ulož a obnov

  1. vytipovat datové atributy ve třídě Hra, které udržují informace o stavu,
  2. projít typy (třídy) těchto atributů a zkontrolovat, zda jsou serializovatelné,
  3. pokud ne (např. třída Mistnost), zkontrolovat zda datové atributy v této třídě jsou serializovatelné, zda předek je serializovatelný – pokud ano, implementovat rozhraní Serializable,
  4. ve třídě Hra vytvořit metody pro uložení stavu hry a načtení stavu hry,
  5. vytvořit třídy PrikazUloz a PrikazObnov

Další možnosti serializace

  • transient – modifikátor, který označuje dočasné datové atributy, které se nemají serializovat (ukládat, obnovovat),
  • do třídy s implementovaným rozhraním Serializable můžete doplnit metody readObject a writeObject – tyto metody poté přebírají odpovědnost za ukládání/obnovu objektu při serializaci, použití:
    • ukládání dodatečných informací,
    • ke změně hodnot před uložením, ke změně hodnot či vyvolání metod po načtení,
  • kontrola verze souboru .class:
    • proměnná static final long serialVersionUID
  • rozhraní Externalizable