Java I/O e serializzazione
Caratteristiche dell I/O in Java Diverse classi per gestire I/O differenti sotto un unica struttura. Possibilità di combinare classi differenti. Modalità di interazione distinte in termini di byte o caratteri.
Classe InputStream Viene ereditata da tutte le classi che rappresentano uno stream in lettura. Fornisce i seguenti metodi: 1 int available(); //Optional 2 void close(); 3 int read(); 4 int read(byte[] b); 5 int read(byte[] b, int off, int len); 6 long skip(long n);
Classe OutputStream Viene ereditata da tutte le classi che rappresentano uno stream in scrittura. Fornisce i seguenti metodi: 1 void close(); 2 void flush(); 3 void write(int b); 4 void write(byte[] b); 5 void write(byte[] b, int off, int len);
Classi FileInputStream e FileOutputStream Implementano lettura e scrittura di file. Costruttori: 1 FileInputStream(File file); 2 FileInputStream(String name); 3 4 FileOutputStream(File file) 5 FileOutputStream(File file, boolean append); 6 FileOutputStream(String name); 7 FileOutputStream(String name, boolean append);
Classe ByteArrayInputStream Tratta un array di byte come un input. Costruttori: 1 ByteArrayInputStream(byte[] buf); //Creates a ByteArrayInputStream so that it uses buf as its buffer array. 2 ByteArrayInputStream(byte[] buf, int offset, int length); //Creates ByteArrayInputStream that uses buf as its buffer array.
Classe ByteArrayOutputStream Tratta un array di byte come un output. È possibile ottenere l array di byte contenente l output tramite: 1 byte[] tobytearray(); //Crea un array contentente l output scritto nello stream.
BufferedInputStream e BufferedOutputStream Queste classi aggiungono un buffer ad un InputStream/OutputStream dato. Inoltre BufferedInputStream aggiunge le funzionalità di mark e reset allo stream.
Classe DataInputStream Aggiunge funzionalità ad un input stream. Attenzione: i metodi operano in binario! Fornisce i seguenti metodi: 1 DataInputStream(InputStream in); 2 3 boolean readboolean(); 4 byte readbyte(); 5 char readchar(); 6 double readdouble(); 7 float readfloat(); 8 int readint(); 9 long readlong(); 10 short readshort(); 11 int readunsignedbyte(); 12 int readunsignedshort(); 13 String readutf(); 14 static String readutf(datainput in);
Classe DataOutputStream Aggiunge funzionalità ad un output stream. Attenzione: i metodi operano in binario! Fornisce i seguenti metodi: 1 DataOutputStream(OutputStream out); 2 3 void writeboolean(boolean v); 4 void writebyte(int v); 5 void writebytes(string s); 6 void writechar(int v); 7 void writechars(string s); 8 void writedouble(double v); 9 void writefloat(float v); 10 void writeint(int v); 11 void writelong(long v); 12 void writeshort(int v); 13 void writeutf(string str);
Reader e Writer Finora abbiamo operato direttamente su sequenze di byte. È possibile ragionare in termini di caratteri tramite le classi Reader e Writer. Tali classi rappresentano un input stream in termini di caratteri.
Classe Reader Viene ereditata da tutte le classi che rappresentano uno stream di caratteri in lettura. Fornisce i seguenti metodi: 1 void close(); //Closes the stream and releases any system resources associated with it. 2 int read(); //Reads a single character. 3 int read(char[] cbuf); //Reads characters into an array. 4 int read(char[] cbuf, int off, int len); //Reads characters into a portion of an array. 5 int read(charbuffer target); //Attempts to read characters into the specified character buffer. 6 boolean ready(); //Tells whether this stream is ready to be read. 7 void reset(); //Resets the stream. 8 long skip(long n); //Skips characters.
Classe Writer Viene ereditata da tutte le classi che rappresentano uno stream di caratteri in scrittura. Fornisce i seguenti metodi: 1 void close(); //Closes the stream, flushing it first. 2 void flush(); //Flushes the stream. 3 void write(char[] cbuf); //Writes an array of characters. 4 void write(char[] cbuf, int off, int len); //Writes a portion of an array of characters. 5 void write(int c); //Writes a single character. 6 void write(string str); //Writes a string. 7 void write(string str, int off, int len); //Writes a portion of a string.
Come passare da I/O su byte a I/O su caratteri? Le classi OutputStreamWriter e InputStreamReader fanno da ponte tra le due. Queste costruiscono un Reader/Writer partendo da un InputStrem/OutputStream. È possibile fornire la codifica da utilizzare, altrimenti verrà usata quella di default.
Classe Scanner Questa classe fornisce metodi utili per parsare un input. Fornisce i seguenti metodi: 1 String findinline(string pattern); 2 String findwithinhorizon(string pattern, int horizon); 3 boolean hasnext(); 4 String next(); 5 String next(string pattern); 6 boolean nextboolean(); 7 byte nextbyte(); 8 double nextdouble(); 9 float nextfloat(); 10 int nextint(); 11 String nextline(); 12 Scanner skip(string pattern); 13 Scanner usedelimiter(string pattern);
Classe PrintWriter Questa classe fornisce metodi utili per stampare un output testuale. Fornisce i seguenti metodi: 1 void print(boolean b); 2 void print(char c); 3 void print(char[] s); 4 void print(double d); 5 void print(float f); 6 void print(int i); 7 void print(long l); 8 void print(object obj); 9 void print(string s); 10 PrintWriter printf(locale l, String format, Object... args); 11 void println(); 12 void println(...); //Stessi metodi di print
Esercizio CSV Si implementi un lettore di file comma-separated values (CSV) Per semplicità si ipotizzi che i caratteri virgola e a capo non siano presenti all interno di alcun valore Fornisce i seguenti metodi: 1 public interface CSVReader { 2 3 public Map<String, String> getrow(int index);//ritorna una riga ( mappa tra i nomi del campo nell intestazione e il valore della riga) 4 5 public int size(); //Ritorna il numero di righe presenti 6 7 }
Esercizio magic number Si vuole identificare se un determinato file è un eseguibile ELF o meno Per semplicità si consideri solo il magic number presente all inizio
Serializzazione Permette di memorizzare, trasmettere via rete una struttura dati
Cosa può essere serializzato? Tipi primitivi, stringhe e istanze di classi serializzabili Riferimenti e classi che rappresentano risorse di sistema (Socket, File,... ) NON possono essere serializzate.
Riferimenti La serializzazione genera una rappresentazione dei dati copiando il contenuto degli oggetti, non i loro riferimenti! Con la deserializzazione otteniamo dei nuovi oggetti, diversi da quelli originali ma con lo stesso valore
Esempio di classe serializzabile 1 public class Person implements Serializable{ 2 3 private static final long serialversionuid = 382104422531955291L; 4 5 private final String name; 6 7 private final Calendar birthday; 8 9 } Implementa Serializable Contiene solo tipi primitivi/oggetti serializzabili
serialversionuid Identifica la versione della classe serializzata Serve per controllare la compatibilità tra diverse versioni della stessa classe che potrebbero avere attributi diversi
Serializzazione Si effettua tramite il metodo writeobject della classe ObjectOutputStream Questo metodo serializza l oggetto passato come parametro e lo scrive su uno stream di uscita
Esempio di serializzazione 1 public static void main(string[] args) throws IOException, ClassNotFoundException { 2 Person person = new Person("Mario", new GregorianCalendar(1990, 11, 20)); 3 ByteArrayOutputStream os = new ByteArrayOutputStream(); 4 ObjectOutputStream out = new ObjectOutputStream(os); 5 out.writeobject(person); //Serializzo 6 }
Deserializzazione Si effettua tramite il metodo readobject della classe ObjectInputStream Questo metodo deserializza il primo oggetto presente nello stream di ingresso Attenzione: Oltre agli errori dovuti alla lettura dallo stream è possibile ottenere una ClassNotFoundException se la classe dell oggetto da deserializzare non viene trovata nel classpath.
Esempio completo di serializzazione 1 public static void main(string[] args) throws IOException, ClassNotFoundException { 2 Person person = new Person("Mario", new GregorianCalendar(1990, 11, 20)); 3 ByteArrayOutputStream os = new ByteArrayOutputStream(); 4 ObjectOutputStream out = new ObjectOutputStream(os); 5 out.writeobject(person); //Serializzo 6 7 ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); 8 ObjectInputStream in = new ObjectInputStream(is); 9 Person p = (Person)in.readObject(); //Deserializzo 10 }
Attributi transient È possibile specificare come transient attributi che non è possibile (o utile) serializzare Tali attributi vengono ignorati dalla serializzazione La deserializzazione (di default) non inizializza gli attributi transient, che assumono il valore null È però possibile modificare il metodo di deserializzazione ridefinendo readobject (analogamente posso ridefinire writeobject)
Esempio di attributi transient 1 public class Person implements Serializable{ 2 private static final long serialversionuid = 382104422531955291L; 3 private final String name; 4 private final Calendar birthday; 5 private transient int age; 6 7 // Posso ridefinire come viene serializzato l oggetto sullo stream 8 private void writeobject(objectoutputstream out) throws IOException { 9 out.defaultwriteobject(); //Utilizzo il comportamento di default 10 } 11 12 // Posso ridefinire come viene deserializzato l oggetto dallo stream 13 private void readobject(objectinputstream in) throws ClassNotFoundException, IOException{ 14 in.defaultreadobject(); //Utilizzo il comportamento di default 15 //Qui posso inizializzare eventuali attributi transient 16 age = computeage(); 17 } 18 }