giovedì 10 giugno 2010

Java: salviamo gli oggetti

Un modo assai più semplice per salvare gli oggetti consiste nell'usare la tecnica della serializzazione della divina scuola SUN Microsystem! Per lo stream di oggetti ci occorre un oggetto ObjectOutputStream, niente di complicato:

FileOutputStream file=new FileOutputStream("file.dat");
ObjectOutputStream output=new ObjectOutputStream(file);

Gli oggetti della classe ObjectOutputStream dispongono di un metodo per la scrittura di oggetti, si tratta del metodo writeObject()! Pertanto, se voglio stampare un oggetto su file avrò nel programma delle righe di codice simile a queste:

FileOutputStream file=new FileOutputStream("file.dat");
ObjectOutputStream output=new ObjectOutputStream(file);
MagicCard a=new MagicCard("Kithkin Araldo",1,3,2);
output.writeObject(a);
output.close();

Non vi sembra semplice? Per la lettura esiste lo stream ObjectInputStream che da agli oggetti della classe il metodo readObject()!

FileInputStream file=new FileInputStream("file.dat");
ObjectInputStream input=new ObjectInputStream(file);
MagicCard a=(MagiCard)input.readObject();

Attenzione, readObject() ritorna oggetti generici e quindi Object! Ecco dunque l'utilità dell'operazione di casting prima di assegnare il valore letto all'oggetto. Se avete smarrito la classe dell'oggetto potete invece chiederla a getClass(). All'inizio parlavo di serializzazione, ricordate?
Ogni classe che intende far scrivere i propri oggetti su file deve implementare l'interfaccia Serializable, tutto qui. E il metodo da sviluppare nella classe per partecipare all'interfaccia? Nessuno. L'interfaccia Serializable non ha metodi! Partecipare a questa interfaccia non costa nulla. Attraverso la tecnica della serializzazione possiamo scrivere oggetti e quindi anche stringe e array (che per Java sono oggetti). E' bello scrivere o leggere un array di oggetti in un colpo solo:

MagicCard[] deck=new MagicCard[40];
...
output.writeObject(deck);

Non dimenticate di chiudere gli stream aperti (con il metodo close()) e di gestire le eventuali eccezioni per l'I/O da file. Vi lascio qualche esempio, una classe che descrive oggetti:

package magic;

import java.io.*;

public class MagicCard implements Serializable {
   public MagicCard (String name,int attack, int defend,int mana) {
      this.name=name;
      this.attack=attack;
      this.defend=defend;
      this.mana=mana;
      this.tap=false;
   }
   public String getName() {
      return this.name;
   }
   public int getAttack() {
      return this.attack;
   }
   public int getDefend() {
      return this.defend;
   }
   public int getMana() {
      return this.mana;
   }
   public boolean isTap() {
      return this.tap;
   }
   public void setAttack(int newAttack) {
      this.attack=newAttack;
   }
   public void setDefend(int newDefend) {
      this.defend=newDefend;
   }
   public void tap() {
      this.tap=true;
   }
   public String toString() {
      String info="Una carta per giocare a Magic! "+
      this.getName()+" è una carta "+this.getAttack()+
      "/"+this.getDefend()+".";
      return info;
   }
   private String name;
   private int attack;
   private int defend;
   private int mana;
   private boolean tap;
   private static final long serialVersionUID = 1L;
}

e una classe che scrive e legge oggetti da file:

import magic.*;

import java.io.*;

public class writeObjectD {
   public static void main(String args[]) {
      MagicCard[] deck=new MagicCard[3];
      deck[0]=new MagicCard("Kithkin Araldo",1,3,2);
      deck[1]=new MagicCard("Cangiante Aviano",2,2,2);
      deck[2]=new MagicCard("Kithkin Cuoregrande",2,1,2);
      System.out.println("Contenuto del deck:");
      for (int i=0;i<deck.length;i++)
         System.out.println(" - "+deck[i].toString());
      try {
         /* scrivo sul file */
         System.out.println("Scrittura su file...");
         FileOutputStream fout=new FileOutputStream("file.dat");
         ObjectOutputStream output=new ObjectOutputStream(fout);
         output.writeObject(deck);
         output.close();
         /* leggo dal file */
         System.out.println("Lettura dal file...");
         FileInputStream fin=new FileInputStream("file.dat");
         ObjectInputStream input=new ObjectInputStream(fin);
         MagicCard[] newDeck=(MagicCard[])input.readObject();
         /* stampo il nuovo deck (letto) */
         System.out.println("Contenuto del deck (letto):");
         for (int i=0;i<newDeck.length;i++)
            System.out.println(" - "+newDeck[i].toString());
         input.close();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
   }
}

La tecnica della serializzazione nasconde al programmatore alcuni retroscena fondamentali! Spesso viene usata senza nemmeno sapere cosa realmente accade, soprattutto quando l'oggetto racchiude in uno o più dei suoi campi i riferimenti ad altri oggetti. La serializzazione di un oggetto attribuisce allo stesso un codice identificativo per permettere di referenziare l'oggetto all'interno del file. Ogni oggetto è serializzato una sola volta (viene cioè verificata la presenza dell'oggetto prima di assegnare un nuovo id) e le successive occorrenze avvengono facendo riferimento al'id assegnato. Questo meccanismo (automatico) impedisce la presenza di più copie dell'oggetto all'interno del file qualora lo stesso risultasse essere presente come campo in più oggetti!

Nessun commento:

Posta un commento