JAVA - I/O System Il JAVA considera tutte i flussi da e verso l’esterno, come stream di byte. Questi possono essere di ingresso o di uscita: 1. InputStream: Flusso di byte in ingresso. Con questa classe è possibile sia leggere il singolo byte, che un certo numero indicato. 2. OutputStream: Flusso di byte in uscita. Anche con questa classe è possibile sia scrivere il singolo byte, che un intero array. Stream Sorgente Filtro Oggetto 1 La classe sorgente Il JAVA divide le possibili sorgenti dei flussi in alcune classi: 1. 2. 3. 4. 5. 6. ByteArrayInputStream: Può essere letta come array di byte StringBufferInputStream: È possibile leggere la stringa corrispondente ai byte ricevuti FileInputStream: Può essere letta come sequenza di byte da un file PipedInputStream: Viene utilizzato per leggere sequenze di byte che provengono da un altro thread SequenceInputStream: Permette di leggere due stream in sequenza Altre sorgenti: Come per esempio i socket, ecc. 2 Il classe filtro Il JAVA mette a disposizione una serie di classi che aggiungono funzionalità alla classe della sorgente: 1. DataInputStream: Viene utilizzata per leggere dati primitivi come int, float, double, ecc. 2. BufferedInputStream: Previene la continua lettura dello stream fisico, implementando un buffer tampone 3. LineNumberInputStream: Tiene traccia del numero di linee che è stato letto dallo stream 4. PushbackInputStream: Mantiene un buffer di un byte, in modo tale da permettere il “push back” di byte letti 3 Esempio import java.io.*; public static void main(String[] args) { try { DataInputStream file = new DataInputStream( new BufferedInputStream( new FileInputStream(“p.txt”))); try { String s; while ((s = file.readLine()) != null) { System.out.println("# " + s); } } finally { file.close(); } } catch (IOException e) { System.out.println(“Errore di apertura"); } } 4 I/O su file Le classi per la gestione dei file sono principalmente due: 1. La classe File Questa classe rappresenta un concetto molto ampio di file-system, in quanto questa classe può non indicare un solo singolo file, ma anche una directory intera, oppure un gruppo di file indicati con una stringa filtro. Con questa classe si possono ottenere tutte le informazioni utili all’accesso dei file e delle directory. Con una stringa filtro si identifica un gruppo di file list() restituisce un array di stringhe contenente i nomi dei file : File path = new File(“.”); String[] list = path.list(); : 5 I/O su file : File f1 = new File(“prova.txt”); File f2 = new File(“prova.old”); f1.renameTo(f2); : Permette di rinominare un file : File f1 = new File(“c:/documenti/prova”); f1.mkdir(); f1.mkdirs(); : Permette di creare interi alberi di directory 6 I/O su file 2. RandomAccessFile Questa classe rappresenta l’insieme di record (delle stesse dimensioni) all’interno di un file. Con essa è possibile posizionarsi su un preciso record, utilizzando il metodo seek(). RandomAccessFile è una classe completamente separata dagli stream, anche se i metodi in comune sono molti. 3. StreamTokenizer Questa classe permette di scomporre in token uno InputStream, facilitando, per esempio, l’analisi sintattica di file aventi una determinata grammatica. 7 JAVA - I/O System Con il rilascio delle nuova versione di JAVA sono state effettuate alcune modifiche sulla gestione degli stream. Sono state introdotte due nuove classi antenate: Reader e Writer. Queste classi introducono funzionalità per la lettura e la scrittura dei caratteri, nonché funzionalità di lettura e scrittura Unicode. Inoltre sono state introdotte classi che permettono la gestione di file zip, gzip e file jar. 8 Serializzazione di Java • Utilizzata da Java Remote Method Invocation per poter passare oggetti e valori di dati primitivi come parametri e risultato di invocazione di metodi • Si usa il meccanismo delle interface Java – se una classe può essere serializzata allora deve implementare la interface Serializable che non contiene alcun metodo (marker interface) • Importante: si assume che chi deserializza un oggetto non ha informazioni a priori: • né sulla classe di cui l’oggetto è istanza • né sulle classi di cui sono istanza i campi dell’oggetto – ma deve avere accesso alle definizioni delle classi (file .class) 9 Struttura della serializzazione di Java • La serializzazione di un oggetto riguarda: – nome della classe, versione, numero, tipi e nomi delle variabili istanza – valori delle variabili istanza • Può contenere anche dei riferimenti ad altri oggetti: – quando un oggetto viene serializzato, tutti gli oggetti a cui fa riferimento sono ricorsivamente (depth-first) serializzati con lui – i riferimenti vengono trasformati in handles (riferimenti all’interno della forma serializzata) • handle per la definizione della classe (per poterla riusare) • handle per i valori degli oggetti istanza delle classi (per poterli riferire) 10 Il problema degli handle (1) • Variabili istanza diverse che fanno riferimento allo stesso oggetto devono usare lo stesso handle • Esempio: – Manager ha una variabile che contiene il riferimento di un Employee che svolge compiti di segreteria per il Manager – supponiamo di avere array staff di 3 Employee con i seguenti valori: 11 Il problema degli handle (2) • Se non si usassero gli handle – quando si deserializza staff, ogni volta che si incontra il riferimento a Harry si ricreerebbe un “nuovo” oggetto 12 L’uso della reflection nella serializzazione • Java utilizza la riflessione per la serializzazione – permettendo, una volta ottenuto il nome della classe ed il tipo dei parametri da passare al costruttore • Nella serializzazione: – si trova il nome della classe dell’oggetto da serializzare – si trovano nome, tipo e valori delle variabili istanza • Nella deserializzazione: – il nome delle classe viene usato per creare la classe – si crea il nuovo costruttore con tipo dei parametri corrispondenti a quelli specificati nella forma serializzata – si usano i valori letti dalla serializzazione come parametri per il costruttore 13 Commenti sulla serializzazione • Perché non tutte le classi sono serializzabili? – sicurezza: un oggetto istanza di una classe puo’ essere scritto in file e “letto”… compreso i dati privati!! – istanze serializzate possono essere alterate e creare situazioni di errori quando la JVM ricarica l’oggetto • Per i campi da non serializzare si inserisce la parola chiave transient davanti alla dichiarazione del campo: – descrittori di file, descrittori di finestre etc. • dipendenti dalla specifica macchina ed inutili da serializzare – campi di cui si vuole mantenere la privatezza assoluta • Esistono altre soluzioni – interface Externalizable che permette di personalizzare il formato di serializzazione (ad es. per crittografare i dati) 14 Esempio deserializzazione public class CitiesNetwork implements Serializable { private static CitiesNetwork me; …… static public CitiesNetwork getInstance() { FileInputStream f; if (me == null){ String currentDirectory=System.getProperty("user.dir"); File storeDir = new File (currentDirectory+"/store"); if (!storeDir.exists()) storeDir.mkdir(); try { f = new FileInputStream (currentDirectory+"/store/CitiesNetwork.obj"); ObjectInputStream ois = new ObjectInputStream (f); me = (CitiesNetwork) ois.readObject(); } catch (FileNotFoundException e) { me = new CitiesNetwork(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace();} } return me; 15 } Esempio serializzazione public void store(){ String currentDirectory = System.getProperty("user.dir"); FileOutputStream f; try { f = new FileOutputStream (currentDirectory+"/store/CitiesNetwork.obj"); ObjectOutputStream oos = new ObjectOutputStream(f); oos.writeObject(this); } catch (FileNotFoundException e) { me = new CitiesNetwork(); } catch (IOException e) { e.printStackTrace(); } } 16