Serializzazione in Java

Posted On 01 Gen 2008
Comment: Off

tutorialjavaTutorial JavaSerializzare un oggetto significa trasformarlo in una sequenza di byte allo scopo di ricostruirlo in un momento successivo. Vediamo in questo articolo come sia possibile con l’uso delle classi in Java gestire, estrarre ed utilizzare i dati in ambienti eterogenei.
Il presente articolo vuole dimostrare come si possano utilizzare i meccanismi di serializzazione messi a disposizione dall‘ambiente di programmazione Java per costruire, salvare su supporti eterogenei, trasmettere lungo un’infrastruttura di rete, oggetti che rappresentano determinate informazioni. A questo punto è importante fornire immediatamente, soprattutto per i meno esperti, la risposta a due domande che sorgono spontanee:
-che cosa intendiamo per “ambiente di programmazione Java”;
-che cosa significa serializzazione.
Per quanto concerne il primo quesito, è importante chiarire come considerare Java un mero linguaggio di programmazione sia sicuramente riduttivo: Java, nelle sue forme di Standard ed Enterprise Edition rappresenta, infatti, un completo ambiente di sviluppo che mette a disposizione del programmatore un grande numero di classi e di librerie in grado di fornire un grande numero di funzionalità. Allo stesso tempo, il fatto che i nostri programmi abbiano bisogno di un particolare interprete (ovviamente la Virtual Machine) per essere eseguiti, rende più idonea la definizione di “ambiente” all’interno del quale progettare, compilare ed eseguire il nostro codice.
Per quanto riguarda la seconda domanda, una risposta sintetica ma allo stesso tempo abbastanza completa, può essere la seguente: serializzare un oggetto significa trasformarlo in una sequenza di byte allo scopo di ricostruirlo in un momento successivo (dopo averne fatto l’uso desiderato, per esempio averlo salvato in un file, in un database, od averlo spedito lungo la rete). Il percorso logico, nella sua forma più semplice, può essere così sintetizzato:

1)si crea una classe per rappresentare una determinata informazione;
2)si utilizzano i metodi e le proprietà della classe per immagazzinare i dati;
3)si salva come sequenza di byte;
4)successivamente si utilizza la classe per estrarre i dati in un formato più facilmente comprensibile.

La condizione necessaria è che sia chi scrive i dati, sia chi successivamente li estrarrà (non necessariamente con la stessa applicazione), devono conoscere come è definita la classe “contenitore”. Di fatto, stiamo costruendo un dialogo tra due o più agenti che condividono una CONVENZIONE, allo scopo di parlare un linguaggio comune. Prenderemo in esame un esempio scritto in Java, ma nulla vieta (anzi lo potremo sperimentare in puntate successive) di costruire lo stesso sistema utilizzando altri linguaggi di programmazione. Allo scopo di far venire al lettore “l’acquolina in bocca”, o quanto meno di stimolarne l’attenzione sulle potenzialmente infinite applicazioni di questa tecnica, possiamo elencare alcune applicazioni, frutto della mia esperienza lavorativa diretta:

-estrazione dei record da un database Mysql e spedizione dell’oggetto creato (e criptato) ad una macchina remota;
-spedizione ad un server remoto di un oggetto che contiene tre immagini Jpeg (small, medium, big) relative alla foto di un certo personaggio, insieme a dei valori stringa che rappresentano diverse informazioni sul personaggio ritratto (nome, professione, ecc.). L’oggetto viene spedito al server dell’agenzia fotografica che salva su disco le immagini e salva in un database le informazioni, allo scopo di fornire un motore di ricerca per il proprio archivio;
-salvataggio dei dati di un piccolo programma gestionale, evitando di dover appoggiarsi ad un database o di dover trafficare con troppi file di testo.

Una “classe” per il pugile
Inizieremo con un esempio abbastanza semplice: costruiremo una classe per memorizzare la foto ed alcune informazioni di un famoso atleta (un grande pugile) e la serializzeremo salvandola su un file. Successivamente, effettueremo l’operazione inversa: l’estrazione di tutti questi dati.

Iniziamo a costruire la classe “contenitore” dei nostri dati:

import java.io.*;

class Pugile implements Serializable { //Abbiamo necessit‡ di implementare l’interfaccia java.io.Serializable

private String nome;
private String cognome;
private String nickname;
private int anni;
private String categoria;
private String record;
private byte[] bImg; //Array di bytes che utilizzeremo per memorizzare la foto in formato .jpg

//Metodi per leggere e scrivere i dati relativi al pugile:

public String getNome() { return this.nome; }

public void setNome(String nome) { this.nome=nome; }

public String getCognome() { return this.cognome; }

public void setCognome(String cognome) { this.cognome=cognome; }

public String getNickname() { return this.nickname; }

public void setNickname(String nickname) { this.nickname=nickname; }

public int getAnni() { return this.anni; }

public void setAnni(int anni) { this.anni=anni; }

public String getCategoria() { return this.categoria; }

public void setCategoria(String categoria) { this.categoria=categoria; }

public String getRecord() { return this.record; }

public void setRecord(String record) { this.record=record; }

//Metodi per salvare ed estrarre i byte della foto .jpg

public byte[] getBImg() { return this.bImg; }

public void setBImg(byte[] bImg) { this.bImg=bImg; }

}

La classe è molto semplice. Rimarchiamo un piccolo particolare: le proprietà sono private, per accedervi in lettura e scrittura utilizzeremo pertanto i metodi (pubblici). In questo modo rispettiamo quello che viene definito il principio dell’ “information hiding”.
Passiamo alle due piccole classi che utilizzeremo per salvare e successivamente estrarre il nostro oggetto. Io utilizzerò un’immagine “tito.jpg” presente sul mio hard-disk. Utilizzate questa (allegata nel CD insieme agli altri file oggetto dell’articolo) o quella che preferite. Per brevità valorizzeremo i dati del pugile (incluso il nome dell’immagine jpg) direttamente nel codice. Ovviamente in un’applicazione “reale” e non di esempio, questi verranno ricevuti dinamicamente. Potete provare, ad esempio, a modificare il codice passandoli da linea di comando come parametri del metodo main().

import java.io.*;

class Scrittore {

private void scrivi() {
try {
Pugile p=new Pugile();
//Scrivo le informazioni:
p.setNome(“Felix”);
p.setCognome(“Trinidad”);
p.setNickname(“Tito”);
p.setAnni(32);
p.setCategoria(“Superwelter”);
p.setRecord(“42-1-0”); //Vittorie-sconfitte-pareggi 😉
//Leggo l’immagine, la salvo in un array di byte e la passo al metodo setBImg()
FileInputStream fis=new FileInputStream(“tito.jpg”);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int c;
while((c=fis.read())!=-1) { baos.write(c); }
p.setBImg(baos.toByteArray());
fis.close();
baos.close();
//Utilizzeremo la classe ObjectOutputStream per serializzare i dati e salvarli nel file tito.dat:
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(“tito.dat”));
oos.writeObject(p);
oos.close();
}
catch(Exception e) {
System.err.println(“Si e’ verificato l’errore: “+e.toString());
}
}

public static void main(String args[]) { new Scrittore().scrivi(); }

}

Anche questa classe è piuttosto semplice. Utilizziamo il metodo writeObject() della classe ObjectOutputStream per serializzare e salvare il nostro oggetto. La gestione delle eccezioni è molto semplificata, ma il nostro è solo un primo esempio!
Passiamo adesso alla terza classe, che leggerà dal file tito.dat l’oggetto serializzato e “magicamente” ci restituirà i nostri dati, oltre a ricostruire l’immagine del pugile salvandola in un file tito2.jpg identico all’originale.

import java.io.*;

class Lettore {

private void leggi() {
try {
//Utilizzeremo la classe ObjectInputStream per leggere l’oggetto serializzato dal file tito.dat:
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(“tito.dat”));
Object obj=ois.readObject();
ois.close();
//E’ necessario un casting, se obj non fosse un’istanza della classe Pugile verrebbe generata una ClassCastException:
Pugile p=(Pugile)obj;
//A questo punto possiamo estrarre i dati e stamparli sullo standard output:
System.out.println(“Nome e cognome: “+p.getNome()+” “+p.getCognome());
System.out.println(“Nickname: “+p.getNickname());
System.out.println(“Anni: “+String.valueOf(p.getAnni()));
System.out.println(“Categoria: “+p.getCategoria());
System.out.println(“Record: “+p.getRecord());
//Non resta che estrarre i byte per ricostruire l’immagine:
byte bImg[]=p.getBImg();
new FileOutputStream(“tito_bis.jpg”).write(bImg);
}
catch(Exception e) {
System.err.println(“Si e’ verificato l’errore: “+e.toString());
}
}

public static void main(String args[]) { new Lettore().leggi(); }

}

Compilate le classi ed eseguite nell’ordine la classe Scrittore e la classe Lettore. Per il nostro piccolo esempio è fondamentale che la classe Pugile (il nostro piccolo “contenitore”) ed il file tito.jpg siano nella stessa directory.

Conclusioni
Come premesso, il presente articolo rappresenta solo una piccola e leggera introduzione ad una tecnica di programmazione che può avere molti sviluppi interessanti. Si tratta di strumenti in grado di sollecitare la fantasia e la curiosità del programmatore, e questa è, a mio parere, una ulteriore buona ragione per guardarvi con attenzione. Un suggerimento può essere quello di provare e riprovare seguendo le proprie intuizioni, allo scopo di fare pratica e di prepararsi al passo successivo, che sarà costituito dalla scrittura di una piccola applicazione client/server in grado di spedire un oggetto serializzato lungo una rete.
Per documentarsi, il riferimento più utile ed immediato è senza dubbio la documentazione della Sun:
-http://java.sun.com/j2se/1.5.0/docs/api/index.html (documentazione delle API, con particolare attenzione al package java.io)
-http://java.sun.com/j2se/1.5.0/docs/guide/serialization/index.html (documentazione e specifiche sulla Object Serialization in Java)



About the Author

Related Posts