Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
con Java.
Lezione 3 - Tipi Generici, Collezioni e IO
TIPI GENERICI
UN PROBLEMA GENERICO
Spesso si desidera costruire classi che possano gestire diversi tipi di oggetti non
accomunati da alcun tipo di gerarchia se non avendo Object come radice.
Prima soluzione: Dichiarare un campo di tipo Object così che esso possa essere
occupato da qualsiasi tipo di oggetto. Problema: nulla impedisce di inserire oggetti
inaspettati causando errori quando un cast incontra un tipo inaspettato.
Seconda soluzione: Creare diverse classi che gestiscono diversi tipi di oggetto.
Problema: Approccio tedioso, spesso insufficiente e incline all’errore.
Classi che utilizzano la classe che usa questa tecnica dovranno essere dichiarate
in simil modo.
DICHIARAZIONE DI TIPI GENERICI
❖ class nomeClasse<T> Dichiarazione tipo generico
❖ E Tipo Parametro
❖ nomeClasse<String> Tipo Parametrizzato (<Tipo Argomento>)
❖ nomeClasse Tipo Grezzo
Usare un Tipo Parametrizzato è un’operazione comunemente nota come
invocazione del tipo generico e specifica al compilatore che si sta usando il Tipo
Grezzo parametrizzato secondo il Tipo Argomento: nessuna nuova classe viene
creata!
ES: Class<String> e Class<Integer> sono la stessa classe invocata tramite diversi
Tipi Argomento. La classe è Cell e il file in cui va salvata è Cell.java!
ESEMPIO DI USO DEI TIPI ARGOMENTO
Nota: Una classe può usare extends per estendere una sola altra classe, mentre più interfacce
possono essere estese da una classe o implementate da interfacce. Il vincolo di tipo può
esprimere tali dipendenze multiple dichiarando la sequenza interlacciata da &. Ad ES:
Bisogna dichiarare liste di elementi che siano compatibili con Number se si vuole
ottenere un effetto simile: il segnaposto ‘?’ seguito dalla keyword extends seguita
dal tipo che si vuole considerare supertipo. Quest’ultimo è chiamato vincolo
superiore.
ESEMPIO D’USO
SUPER SEGNAPOSTO
È possibile usare i segnaposto per comunicare al compilatore che si possono
usare anche supertipi del tipo indicato, oltre che il tipo indicato.
ES:
METODI GENERICI
Similmente ai costruttori, anche i metodi possono introdurre tipi generici,
dichiarandoli tra i modificatori ed il tipo restituito come:
ES:
❏ Impossibile istanziare (new T()) e dichiarare array di tale tipo (new T[n])
❏ No array il cui tipo di elementi è il tipo parametrizzato (new
Bevanda<Acqua>[1]), eccetto esso è tale che tutti gli argomenti sono di tipo
segnaposto non vincolato (new Bevanda<?>[1])
❏ Una classe generica non può estendere Throwable: le eccezioni generiche
non sono intercettabili.
OVERLOADING E OVERRIDING
Nuova definizione: stessa segnatura per due metodi generici indica che hanno lo
stesso numero di variabili di tipo dotate degli stessi vincoli. Inoltre, rinominando
tutte le occorrenze delle variabili di tipo del secondo metodo con le corrispondenti
del primo, i tipi dei parametri formali devono coincidere.
Due metodi generici sono sovraccaricati se hanno lo stesso nome e non hanno
segnature overriding-equivalent. In caso contrario si ha un errore!
ESEMPIO OVERLOADING
OVERLOADING ERRATI
Aggiungere metodi generici la cui erasure è uguale a quella dei metodi dichiarati
genera errori. Ad ES:
❖ void m(Object o) { }
❖ void m(Number n) { }
❖ <G extends String> void m(G g) { }
❖ void m(Bevanda<Object> q) { }
OVERRIDING
Un metodo interno ad un sottotipo ridefinisce potenzialmente un metodo
accessibile all’interno del supertipo se i due metodi hanno ugual nome e sono
override-equivalent. La segnatura del metodo della sottoclasse deve essere la
stessa di quella del metodo della superclasse o della sua erasure.
Overriding a senso unico: un metodo senza tipo generici può ridefinirne uno con,
ma non viceversa!
ESEMPIO OVERRIDE
EURISTICA GENERALE
Quando si pensa di fare un overload o un override bisogna sempre tenere a
mente la erasure delle segnature dei metodi e nel ricordare che non è possibile
aumentare la genericità di un metodo ereditato.
ESTENDERE CLASSI E TIPI GENERICI
★ Si può estendere un tipo non generico per produrre un sottotipo che può essere sia
generico che non.
★ Si può estendere un tipo generico per produrre un sottotipo generico.
★ Si può estendere un tipo parametrizzato particolare per produrre un sottotipo non
generico.
★ Si possono combinare le penultime due opzioni.
Attenzione quando si estendono tipi parametrizzati! Valutare le conseguenze, in
particolar modo se si implementano interfacce poiché esse non possono avere
parametrizzazioni diverse!
ES: Se ad un certo punto definisco la classe Value e implemento Comparable<Value>,
poi non posso implementare Comparable<ExtendedValue> in una classe ExtendedValue
che estende Value!
COLLEZIONI
COLLECTION FRAMEWORK
Collection è un framework (insieme di strumenti SW da cui l’utente può partire
per creare programmi) di Java che fornisce un’architettura adatta ad
immagazzinare e gestire gruppi di oggetti, realizzando di fatto delle strutture dati.
Le maggiori operazioni fornite da questo framework sono:
➢ Ricerca
➢ Ordinamento
➢ Inserimento
➢ Modifica
➢ Eliminazione
Vedremo interfacce e classi fornite.
STRUTTURA DI COLLECTION
ARRAYLIST
ArrayList è una collezione che funziona in modo simile ad un array, ma a
differenza di questi ultimi essa è dinamica: può avere un numero indefinito di
elementi.
Si trova nel package java.util e va importata nella classe che la vuole usare.
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/ArrayList.ht
ml
ARRAYLIST - PROPRIETÀ FONDAMENTALI
★ Può contenere elementi duplicati.
★ Mantiene l’ordine di inserimento.
★ Non è sincronizzata.
★ Permette accesso casuale ai suoi elementi basato su indice.
★ Manipolazione più lenta di LinkedList.
ARRAYLIST - COSTRUTTORI
❖ ArrayList()
➢ Crea una struttura vuota.
❖ ArrayList(Collection<? extends E> c)
➢ Crea una struttura partendo dalla collezione c.
❖ ArrayList(int capacity)
➢ Crea una struttura con capacità iniziale settata a capacity.
ES: lista.add(“Ciao!”);
ARRAYLIST - ACCESSO E MANIPOLAZIONE
È possibile accedere agli elementi della lista usando:
❖ E get(int index)
➢ Restituisce il valore dell’elemento di tipo E in posizione index.
■ ES: lista.get(0);
➔ Ciclo for combinato con metodo size() che restituisce il numero di elementi
della lista.
◆ for(int i = 0; i < lista.size(); i++) System.out.println(lista.get(i));
➔ Ciclo for-each
◆ for(E e : lista) System.out.println(“E”);
L’iteratore ora contiene tutti gli elementi della lista e punta al primo elemento.
METODI DI ITERATOR
❖ boolean hasNext()
➢ Ritorna true se l’iteratore ha elementi ancora da iterare.
■ ES: iter.hasNext();
❖ Object next()
➢ Fa tornare l’elemento puntato dal cursore e muove il cursore al prossimo.
■ ES: String s = iter.next();
❖ void remove()
➢ Rimuove l’ultimo elemento fatto tornare dall’iteratore.
■ ES: iter.remove(); //Rimuove l’elemento dalla collezione!
ARRAYLIST - ITERARE CON ITERATOR
ARRAYLIST - ORDINAMENTO
Collections mette a disposizione un metodo in grado di ordinare liste di oggetti
che implementino l’interfaccia Comparable:
❖ Collections.sort();
➢ Metodo statico di Collections (java.util.Collections) che ordina gli elementi in base al loro
criterio.
■ Tipi base: ordine numerico (di codifica utf-8 per i caratteri).
■ Stringhe: ordine lessicografico.
■ Tutto il resto: compareTo().
ES: Collections.sort(lista);
ARRAYLIST - ALTRI METODI
❖ boolean isEmpty()
➢ Ritorna true se la lista riferita non contiene elementi.
❖ boolean contains(Object o)
➢ Ritorna true se la lista riferita contiene l’elemento o.
❖ int size()
➢ Restituisce il numero di elementi attualmente nella lista.
❖ int indexOf(Object o)
➢ Restituisce l’indice della prima occorrenza di o. -1 se non presente.
❖ int lastIndexOf(Object o)
➢ Restituisce l’indice dell’ultima occorrenza di o. -1 se non presente.
❖ Object[] toArray()
➢ Restituisce un array equivalente all’ArrayList.
ESERCIZI
● Partendo dall’ultima ArrayList generata, iterarla con un for-each e sostituire
ogni numero dispari con la decina successiva.
● Ordinare la lista ottenuta.
● Stampare a schermo la lista, iterandola mediante un Iterator.
● Cancellare tutti gli elementi non primi e non uguali a 50 della lista. Se al
termine la lista è vuota, comunicarlo a schermo.
● Restituire l’indice della prima e dell’ultima occorrenza di 50, stampando “Non
presente” in caso la lista non contenga 50.
LINKEDLIST
Una lista concatenata è una lista di elementi disposti in preciso ordine. L’idea di
una lista di questo tipo è che ogni suo nodo contenga un elemento (o una lista di
elementi) e un puntatore al nodo successivo.
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/LinkedList.ht
ml
LINKEDLIST - PROPRIETÀ FONDAMENTALI
★ Può contenere elementi duplicati.
★ Mantiene l’ordine di inserimento.
★ Non è sincronizzata.
★ Manipolazione più rapida di ArrayList.
★ Può essere usata come lista, come pila o come coda.
LINKEDLIST - COSTRUTTORI
❖ LinkedList()
➢ Crea una lista concatenata vuota.
❖ LinkedList(Collection <? extends E> c)
➢ Crea una lista concatenata a partire dalla collezione c.
Così come ArrayList, anche LinkedList usa i tipi generici e per questo deve essere
dichiarata in un modo simile al seguente:
È possibile consultare le slide viste per ArrayList per avere un’idea generale sui
metodi utilizzabili con questa classe: merito dell’implementazione comune
dell’interfaccia List!
In realtà LinkedList offre più metodi, alcuni dei quali hanno però funzionalità molto
simili tra loro: questo perché LinkedList viene usata per rappresentare un buon
numero di strutture dati!
LISTA CONCATENATA
Lista composta da nodi in cui ogni nodo contiene uno o più elementi e un
puntatore al nodo successivo.
L’accesso alla lista non è casuale! Seppure possiamo lavorare su punti precisi
della lista, il sistema scorre tutti gli elementi dal primo a quello trovato! La
manipolazione è veloce ma l’accesso è lento!
LISTA DOPPIAMENTE CONCATENATA
È come una lista concatenata ma ogni nodo ha anche un puntatore al nodo
precedente. Si può scorrere in entrambe le direzioni, velocizzando la ricerca in
una lista ordinata se cerchiamo un elemento che sappiamo essere più vicino al
massimo!
ITERARE UNA LISTA DOPPIAMENTE CONCATENATA
Per iterare nella direzione standard è possibile usare un Iterator generato dal
metodo iterator() già visto per ArrayList.
❖ Iterator<E> descendingIterator()
➢ Restituisce un Iterator che scorre la lista in ordine inverso.
■ ES: Iterator revit = concat.descendingIterator();
ESERCIZI
● Creare una classe ListaDoppiamenteConcatenata che crea una lista
doppiamente concatenata di 10 interi compresi tra 0 e 100 generati in modo
casuale.
● Stampare il contenuto della lista sia da sinistra a destra che da destra a
sinistra.
● Cercare i metodi adatti per rimuovere il primo e l’ultimo elemento della lista.
● Aggiungere altri due elementi casuali tra 0 e 100 alla coda destra della lista.
PILA
Struttura dati conosciuta anche come stack che obbedisce alla politica LIFO (Last
In First Out). Le operazioni su questa struttura sono limitate e hanno nomi precisi:
ARRAYLIST LINKEDLIST
Usa un array dinamico per contenere i dati. Usa una lista doppiamente concatenata per
contenere i dati.
Manipolazione lenta per via degli shift. Manipolazione veloce per via dell’assenza di shift.
Agisce solamente come lista. Agisce come lista, come coda e come pila (e altre!)
Da lista a array:
❖ boolean offer(E e)
➢ Aggiunge l’elemento e alla coda.
❖ E poll()
➢ Restituisce e rimuove l’elemento in testa alla coda, null se la coda è vuota.
❖ E peek()
➢ Restituisce senza rimuovere l’elemento in testa alla coda, null se la coda è vuota.
PRIORITY QUEUE
PriorityQueue è una classe che implementa Queue: il suo scopo è quello di
realizzare una coda in cui gli elementi non sono ordinati secondo una politica
FIFO ma secondo una politica basata su priorità: ad ogni elemento è associato un
valore comparabile di priorità e gli elementi con priorità più alta escono per primi
dalla coda.
Le operazioni di poll() eliminano prima gli elementi reputati minori dal metodo
compareTo() che agisce come decisore del grado di priorità.
PRIORITYQUEUE - ESEMPIO
HASHSET
Un set è una lista che ha una regola aggiuntiva: non può contenere chiavi
duplicate.
Tale struttura dati è rappresentata da un’interfaccia Set (che nulla ha che vedere
con List, ma è molto simile).
HashSet è una classe che implementa tale interfaccia e che garantisce un rapido
accesso ai suoi elementi grazie ad una tecnica chiamata “hashing” in cui ad ogni
valore è associata una chiave univoca usata per accedervi e che viene usata per
ordinare il set.
HASHSET - PROPRIETÀ FONDAMENTALI
★ Usa l’hashing per memorizzare i dati
★ Contiene solo valori unici
★ Permette valori nulli
★ Non è sincronizzabile
★ Non mantiene l’ordine di inserimento, lasciando tale responsabilità alla chiave
★ Miglior approccio per le operazioni di ricerca
★ Capacità iniziale: 16
★ Fattore di carico (quando incremento la capacità?): 0,75 (75% dell’attuale)
HASHSET - COSTRUTTORI
❖ HashSet()
➢ Crea un HashSet vuoto di default (capacità 16 e loading factor 0,75).
❖ HashSet(int capacity)
➢ Crea un HashSet vuoto con capacità iniziale pari a capacity e loading factor 0,75.
❖ HashSet(int capacity, float loadfact)
➢ Crea un HashSet vuoto con capacità iniziale pari a capacity e loading factor loadfact.
❖ HashSet(Collection <? extends E> c)
➢ Crea un HashSet a partire dalla collezione c.
add(), remove(), iterator() e molti altri sono presenti come metodi di HashSet e
permettono di manipolarlo in modo simile ad una lista. Si noti che aggiungere un
elemento già presente fa semplicemente fallire l’add, ma non causa errori!
ESERCIZI
● Una biblioteca ha necessità di catalogare i propri libri ma ne ha davvero tanti.
Per questo motivo l’amministrazione ha deciso di catalogare prima i libri con
più pagine. Creare una classe Libro che abbia campi per rappresentare un ID,
il titolo, il numero di pagine e l’autore e creare una classe LibroDemo che
aggiunga una serie di libri ad una coda con priorità. Effettuare il dequeue di
tutti i libri stampandone a schermo i dettagli ogni volta che un libro lascia la
coda.
● A partire dalla classe ArrayLists, creare un HashSet a partire dalla collezione
realizzata e ricercare l’elemento massimo di tale collezione.
MAPPE
Una mappa è una struttura dati che fa corrispondere ad un elemento di un certo
tipo detto chiave un altro elemento di un altro tipo detto valore.
Per supportare questa struttura dati esistono due interfacce: Map e SortedMap (la
seconda è sottointerfaccia della prima).
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/HashMap.html
HASHMAP - COSTRUTTORI
❖ HashMap()
➢ Genera una HashMap con valori di capacità e fattore di carico di default (16 e 0,75).
❖ HashMap(Map <? extends K, ? extends V> m)
➢ Genera una HashMap a partire da un altro elemento che implementa Map.
❖ HashMap(int capacity)
➢ Genera una HashMap con capacità capacity e fattore di carico di default.
❖ HashMap(int capacity, float loadfact)
➢ Genera una HashMap con capacità capacity e fattore di carico loadfact.
❖ V remove(Object key)
➢ Rimuove la entry con la chiave specificata, facendo ritornare il valore così eliminato.
❖ boolean remove(Object key, Object value)
➢ Rimuove la entry con la chiave specificata solo se il valore è quello atteso, facendo ritornare
true o false a seconda che l’eliminazione sia avvenuta o meno.
❖ void clear()
➢ Svuota la HashMap.
HASHMAP - ACCESSO E RIMPIAZZO
Ovviamente, è possibile accedere in modo diretto ai valori in base alla chiave.
❖ V get(Object key)
➢ Restituisce il valore associato alla chiave key; null se tale chiave non è presente.
❖ V replace(K key, V value)
➢ Rimpiazza il valore associato a key con value, restituendo il vecchio valore.
❖ boolean replace(K key, V oldValue, V newValue)
➢ Rimpiazza il valore associato a key con newValue solo se il valore precedente è quello
specificato in oldValue. Restituisce true se la sostituzione avviene, altrimenti restituisce false.
HASHMAP - ALTRI METODI
❖ boolean isEmpty()
➢ Ritorna true se la HashMap non contiene entry.
❖ boolean containsValue(Object value)
➢ Ritorna true se la HashMap contiene almeno una entry che abbia valore value.
❖ boolean containsKey(Object key)
➢ Ritorna true se la HashMap contiene almeno una entry che abbia chiave key.
❖ int size()
➢ Restituisce il numero di entry della HashMap.
ESERCIZI
● Creare una HashMap che contenga 10 valori interi generati casualmente tra 0
e 100 associati a delle chiavi a loro volta intere. Stampare la HashMap a
schermo.
● A partire dalla prima HashMap, crearne una seconda contenente tutti gli
elementi della prima e aggiungervi 8 valori casuali aggiuntivi. Stampare a
schermo, per ogni entry, il valore se questo è dispari, la somma tra il valore e
la chiave se il valore è pari (e sostituirlo al valore attuale) e rimuovere l’entry
senza stamparla se la chiave è un numero primo.
CENNI AL HASHCODE
In passato abbiamo visto come il metodo ereditato da Object toString() se non
ridefinito restituisca una stringa composta dal seguente formato:
“NomeClasse@hashcode”
★ I metodi statici offrono algoritmi che si comportano in modo polimorfico per operare
sulle collezioni.
★ Passare collezioni o oggetti uguali a null ai metodi di questa classe causa
un’eccezione di NULLPOINTEREXCEPTION.
Per la documentazione:
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Collections.html
ESERCIZI
● Scrivere una classe CollezioniMetodi che crei una ArrayList di valori double
diversi da 0 richiesti in input all’utente. I valori vengono richiesti fino a che
l’utente non inserisce uno 0.
● Aggiungere 3 elementi alla lista: 12.0, 24.0 e 36.0 usando il metodo
opportuno della classe Collections. Riordinare quindi la lista in ordine
naturale.
● Trovare il massimo e il minimo della lista utilizzando i metodi della classe
Collection.
● Scambiare di posto il primo e l’ultimo elemento della lista.
I/O
TUTTO FLUISCE
Il traffico di I/O non avviene tutto in una volta, piuttosto avviene come una
sequenza di dati, detta stream (flusso).
Uno stream è composto da bytes e viene anche chiamato bytestream per tale
motivo. L’idea è che il flusso continui a trasportare dati dal momento in cui viene
aperto a quello in cui viene chiuso. Alle volte il flusso è chiamato anche “canale”.
Java ha 3 tipi di flusso sempre attivi che si occupano di gestire le comunicazioni
con la console:
★ System.in Canale standard di input
★ System.out Canale standard di output
★ System.err Canale standard di errore
ESEMPI DI USO
GESTIONE DELL’IO
Tutte le classi volte a gestire l’IO in Java sono incluse nel package java.io e
vanno quindi importate nella classe prima di poter essere utilizzate.
I dati possono provenire (input) ed andare (output) da/a file di tipo diverso.
L’idea è che i dati vengano letti da una sottoclasse di InputStream e scritti da una
classe di OutputStream dopo essere stati sottoposti alle volute manipolazioni.
FILE SYSTEM
Ogni SO odierno implementa una struttura famosamente conosciuta con il nome di File
System. Il funzionamento di questo sistema è complesso e oggetto di molti corsi sui
Sistemi Operativi, ma a noi basta sapere la proprietà fondamentale di questo sistema:
Tutto ciò che è gestito dal Sistema Operativo è visto come un file, ovvero una collezione
di dati.
Questo implica che anche le periferiche di input e output vengono viste come files e per
questo il File Handling può essere gestito dagli stream a prescindere dal tipo di sorgente
e destinazione.
OUTPUTSTREAM
OutputStream è una classe astratta che agisce come capostipite della gerarchia delle classi di gestione
dell’output. Ogni sottoclasse di OutputStream è una sua specializzazione.
Metodi Fondamentali:
Metodi Fondamentali:
Si noti il parallelismo!
FILE IN JAVA
La classe File di Java consente di rappresentare astrattamente il nome di un path
(assoluto o relativo) di un file o di una directory. Tale path è conosciuto come
“nome del file”.
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/io/File.html
❖ File(String pathname)
➢ Crea un oggetto contenente il pathname (assoluto o relativo) fornito come argomento.
METODI PRINCIPALI DI GESTIONE DEI FILE
❖ boolean createNewFile()
➢ Crea un nuovo file vuoto al pathname riferito e solo se tale file non esiste. Restituisce true se il file è stato creato.
❖ boolean mkdir()
➢ Crea una directory al pathname fornito.
❖ boolean canWrite()
➢ Controlla se il file riferito dal pathname è scrivibile dall’applicazione.
❖ boolean canRead()
➢ Controlla se il file riferito dal pathname è leggibile dall’applicazione.
❖ boolean canExecute()
➢ Controlla se il file riferito dal pathname è eseguibile dall’applicazione.
❖ boolean isDirectory() / isFile()
➢ Controlla se il pathname si riferisce ad una directory/file.
❖ String getName()
➢ Restituisce il nome del file o della directory riferita dal pathname.
❖ String[] list()
➢ Restituisce un array di stringhe rappresentanti i nomi dei file e delle directory al pathname della directory fornito.
❖ String[] listFiles()
➢ Restituisce un array di stringhe rappresentanti i nomi dei file al pathname della directory fornito.
ESERCIZI
● Scrivere una classe FileTest che crei un file vuoto nella directory corrente e
stampi su standard output se il file è stato creato o meno.
● Creare altri 3 file vuoti nella directory corrente, quindi stampare su standard
output il nome di tutti i file presenti nella directory.
● Creare una nuova directory nella directory corrente. Stampare i nomi di tutti i
file e di tutte le directory nella directory corrente su standard output.
● Stampare informazioni riguardo la possibilità di eseguire/leggere/scrivere i file
della directory corrente e anche il loro peso in byte (usare il metodo length()).
FILEOUTPUTSTREAM
FileOutputStream è una classe che gestisce un flusso direzionato ad un file per
scrivere in esso.
Utile per scrivere tipi primitivi tramite un flusso di byte. Per dati orientati ai
caratteri, è meglio usare la classe FileWriter.
Importabile tramite import java.io.FileOutputStream.
Link alla documentazione:
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/io/FileOutputStr
eam.html
FILEOUTPUTSTREAM - COSTRUTTORI
❖ FileOutputStream(File f)
➢ Crea uno stream di output verso il file f.
❖ FileOutputStream(String s)
➢ Crea uno stream di output verso il file nominato come s.
Anche in questo caso vengono sollevate eccezioni di IO, quindi bisogna gestirle
così come visto per FileOutputStream e FileInputStream!
FILEWRITER - METODI
❖ void write(String text)
➢ Scrive text nel file.
❖ void write(char c)
➢ Scrive c nel file.
❖ void write(char[] cs)
➢ Scrive l’array di caratteri nel file.
❖ void flush()
➢ Svuota lo stream.
❖ void close()
➢ Chiude lo stream.
FILEREADER
FileReader è la classe java che si occupa di leggere sequenze di caratteri dai file.
La lettura avviene in modo simile a FileInputStream e quindi i caratteri arrivano al
programma sotto forma di bytes e quindi vanno riconvertiti in caratteri.
Può essere utile utilizzare la classe StringBuilder per costruire stringhe a partire
dai caratteri convertiti.
Importabile tramite import java.io.FileReader.
Link alla documentazione:
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/io/FileReader.ht
ml
ESERCIZI
● Scrivere una classe StampaCaratteri che usi FileReader e FileWriter per
leggere 4 stringhe distribuite su 4 righe di un file.
● Stampare in un nuovo file “caratteri.txt” i caratteri delle 4 stringhe secondo il
seguente criterio: primo carattere della prima stringa, primo carattere della
seconda stringa, primo carattere della terza stringa, etc… fino a che tutti i
caratteri sono stati stampati. Ricordarsi di gestire il caso in cui le stringhe
hanno diverse dimensioni.
IO E OGGETTI: LA SERIALIZZAZIONE
Il concetto di serializzazione in Java indica la capacità di codificare lo stato di un
oggetto (i valori attuali dei suoi campi) in modo tale da poterlo scrivere in un flusso
di byte.
La conversione inversa da flusso di byte allo stato dell’oggetto attuale viene
chiamata deserializzazione.
La comodità di questa tecnica è che è indipendente dalla piattaforma: possiamo
serializzare un oggetto su un sistema e deserializzarlo su un altro! Portabilità
massima, soprattutto per la comunicazione in rete (marshalling).
Per poter essere serializzato, un oggetto deve implementare l’interfaccia
Serializable.
L’INTERFACCIA SERIALIZABLE
L’interfaccia Serializable ha un corpo davvero difficile da ricordare:
Serializable è una delle così dette marker interfaces: interfacce il cui unico scopo
è comunicare che gli oggetti di una determinata classe possano subire il
serializzazione. Per implementarla bisogna importare java.io.Serializable.
Per poter rendere serializzabili gli oggetti di una classe, si deve semplicemente
scrivere:
OBJECTOUTPUTSTREAM
ObjectOutputStream è la classe di Java che permette di scrivere in un flusso di
output degli oggetti che poi potranno essere scritti dalla classe ObjectInputStream.
La codifica dell’oggetto avviene in base al nome e ai valori dei suoi campi. Essa
può essere scritta in un file o trasferita in una rete. L’oggetto deve essere
serializzabile.
Bisogna importare java.io.ObjectOutputStream per utilizzare la classe.
Link alla documentazione:
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/io/ObjectOutput
Stream.html
OBJECTOUTPUTSTREAM - COSTRUTTORI
❖ ObjectOutputStream(OutputStream out) throws IOException
➢ Crea uno stream di output per oggetti che scrive in uno specifico output stream
Ad ES:
Ad ES:
Per quanto riguarda le collezioni o anche solo gli array, per essere serializzabili
devono essere serializzabili tutti gli elementi contenuti.
TRANSIENT
La keyword transient altro non è che un modificatore al pari di final, abstract,
etc…
Lo scopo di transient è quello di marcare quei membri della classe che non
vogliono essere coinvolti nel processo di serializzazione.
...un attimo!
Questi metodi in realtà sono i metodi che abbiamo sempre utilizzato al termine di
System.out!
PRINTSTREAM E SYSTEM
La classe System come riportato ad inizio di questa sezione rappresenta già uno
stream standard sempre presente orientato agli standard input, output e canale di
errore.
BufferedReader legge solo testi e lo si fa con br.readLine(), ma con l’uso dei vari metodi di parseType…