1. Flussi di dati
Raramente un programma esiste come un'isola a sé stante. I programmi di solito interagiscono in qualche modo con il "mondo esterno". Ciò può avvenire attraverso la lettura di dati dalla tastiera, l'invio di messaggi, il download di pagine da Internet o, al contrario, il caricamento di file su un server remoto.
Possiamo riferirci a tutti questi comportamenti in una parola: scambio di dati tra il programma e il mondo esterno. Aspetta, non è solo una parola.
Naturalmente, lo scambio di dati stesso può essere suddiviso in due parti: ricevere dati e inviare dati. Ad esempio, leggi i dati dalla tastiera usando un Scanner
oggetto: questo sta ricevendo dati. E visualizzi i dati sullo schermo usando un System.out.println()
comando: questo sta inviando dati.
Nella programmazione, il termine "stream" viene utilizzato per descrivere lo scambio di dati. Da dove viene quel termine?
Nella vita reale, puoi avere un flusso d'acqua o un flusso di coscienza. Nella programmazione, abbiamo flussi di dati .
Gli stream sono uno strumento versatile. Consentono al programma di ricevere dati da qualsiasi luogo (flussi di input) e inviare dati ovunque (flussi di output). Quindi, ci sono due tipi:
- Un flusso di input serve per ricevere dati
- Un flusso di output serve per l'invio di dati
Per rendere i flussi "tangibili", i creatori di Java hanno scritto due classi: InputStream
e OutputStream
.
La InputStream
classe ha un read()
metodo che ti consente di leggere i dati da essa. E la OutputStream
classe ha un write()
metodo che ti consente di scrivere dati su di essa. Hanno anche altri metodi, ma ne parleremo più avanti.
Flussi di byte
Di che tipo di dati stiamo parlando? Che formato prende? In altre parole, quali tipi di dati supportano queste classi?
Queste sono classi generiche, quindi supportano il tipo di dati più comune: il byte
. An OutputStream
può scrivere byte (e array di byte) e un InputStream
oggetto può leggere byte (o array di byte). Ecco fatto: non supportano altri tipi di dati.
Di conseguenza, questi flussi sono anche chiamati flussi di byte .
Una caratteristica degli stream è che i loro dati possono essere letti (o scritti) solo in sequenza. Non puoi leggere i dati nel mezzo di un flusso senza leggere tutti i dati che lo precedono.
Ecco come funziona la lettura dei dati dalla tastiera attraverso la Scanner
classe: leggi i dati dalla tastiera in sequenza, riga per riga. Leggiamo una riga, poi la riga successiva, poi la riga successiva e così via. Opportunamente, il metodo per leggere le righe è chiamato nextLine()
.
Anche la scrittura dei dati su un OutputStream
avviene in sequenza. Un buon esempio di ciò è l'output della console. Emetti una riga, seguita da un'altra e un'altra ancora. Questo è un output sequenziale. Non puoi emettere la prima riga, poi la decima e poi la seconda. Tutti i dati vengono scritti in un flusso di output solo in sequenza.
Flussi di caratteri
Di recente hai appreso che le stringhe sono il secondo tipo di dati più popolare, e in effetti lo sono. Molte informazioni vengono trasmesse sotto forma di caratteri e intere stringhe. Un computer eccelle nell'inviare e ricevere tutto come byte, ma gli umani non sono così perfetti.
Tenendo conto di questo fatto, i programmatori Java hanno scritto altre due classi: Reader
e Writer
. La Reader
classe è analoga alla InputStream
classe, ma il suo read()
metodo non legge byte, ma caratteri ( char
). La Writer
classe corrisponde alla OutputStream
classe. E proprio come la Reader
classe, funziona con i caratteri ( char
), non con i byte.
Se confrontiamo queste quattro classi, otteniamo la seguente immagine:
Byte (byte) | Caratteri (carattere) | |
---|---|---|
Lettura dei dati |
|
|
Scrivere dati |
|
|
Applicazione pratica
Le classi InputStream
, OutputStream
, Reader
e Writer
non sono utilizzate direttamente da nessuno, poiché non sono associate ad alcun oggetto concreto da cui i dati possono essere letti (o in cui i dati possono essere scritti). Ma queste quattro classi hanno molte classi discendenti che possono fare molto.
2. InputStream
classe
La InputStream
classe è interessante perché è la classe madre di centinaia di classi discendenti. Non ha dati propri, ma ha metodi che ereditano tutte le sue classi derivate.
In generale, è raro che gli oggetti stream memorizzino i dati internamente. Uno stream è uno strumento per la lettura/scrittura di dati, ma non per l'archiviazione. Detto questo, ci sono eccezioni.
Metodi della InputStream
classe e di tutte le sue classi discendenti:
Metodi | Descrizione |
---|---|
|
Legge un byte dal flusso |
|
Legge una matrice di byte dal flusso |
|
Legge tutti i byte dal flusso |
|
Salta n i byte nel flusso (li legge e li scarta) |
|
Controlla quanti byte sono rimasti nel flusso |
|
Chiude il flusso |
Esaminiamo brevemente questi metodi:
read()
metodo
Il read()
metodo legge un byte dal flusso e lo restituisce. Potresti essere confuso dal int
tipo restituito. Questo tipo è stato scelto perché int
è il tipo intero standard. I primi tre byte di int
saranno zero.
read(byte[] buffer)
metodo
Questa è la seconda variante del read()
metodo. Ti consente di leggere un array di byte da un InputStream
tutto in una volta. L'array che memorizzerà i byte deve essere passato come argomento. Il metodo restituisce un numero, il numero di byte effettivamente letti.
Diciamo che hai un buffer di 10 kilobyte e stai leggendo i dati da un file usando la FileInputStream
classe. Se il file contiene solo 2 kilobyte, tutti i dati verranno caricati nell'array del buffer e il metodo restituirà il numero 2048 (2 kilobyte).
readAllBytes()
metodo
Un ottimo metodo. Legge solo tutti i dati da InputStream
fino a quando non si esaurisce e li restituisce come un array a byte singolo. Questo è molto utile per leggere file di piccole dimensioni. I file di grandi dimensioni potrebbero non adattarsi fisicamente alla memoria e il metodo genererà un'eccezione.
skip(long n)
metodo
Questo metodo consente di saltare i primi n byte dall'oggetto InputStream
. Poiché i dati vengono letti rigorosamente in sequenza, questo metodo legge semplicemente i primi n byte dal flusso e li scarta.
Restituisce il numero di byte che sono stati effettivamente saltati (nel caso in cui il flusso sia terminato prima che n
i byte venissero saltati).
int available()
metodo
Il metodo restituisce il numero di byte che sono ancora rimasti nel flusso
void close()
metodo
Il close()
metodo chiude il flusso di dati e rilascia le risorse esterne ad esso associate. Una volta chiuso un flusso, non è più possibile leggere altri dati da esso.
Scriviamo un programma di esempio che copia un file molto grande. Non possiamo usare il readAllBytes()
metodo per leggere l'intero file in memoria. Esempio:
Codice | Nota |
---|---|
|
InputStream per la lettura dal file OutputStream per la scrittura nel file Buffer in cui leggeremo i dati Finché ci sono dati nel flusso Leggere i dati nel buffer Scrivere i dati dal buffer nel secondo flusso |
In questo esempio, abbiamo utilizzato due classi: FileInputStream
è un discendente di InputStream
per la lettura dei dati da un file ed FileOutputStream
è un discendente di OutputStream
per la scrittura dei dati in un file. Parleremo della seconda classe un po 'più tardi.
Un altro punto interessante qui è la real
variabile. Quando l'ultimo blocco di dati viene letto da un file, potrebbe facilmente contenere meno di 64 KB di dati. Di conseguenza, non dobbiamo emettere l'intero buffer, ma solo una parte di esso: i primi real
byte. Questo è esattamente ciò che accade nel write()
metodo.
3. Reader
classe
La Reader
classe è un analogo completo della InputStream
classe. L'unica differenza è che funziona con i caratteri ( char
), non con i byte. Proprio come la InputStream
classe, la Reader
classe non viene utilizzata da nessuna parte da sola: è la classe genitore per centinaia di classi discendenti e definisce metodi comuni per tutte.
Metodi della Reader
classe (e di tutte le sue classi discendenti):
Metodi | Descrizione |
---|---|
|
Legge uno char dal flusso |
|
Legge una char matrice dal flusso |
|
Salta n chars nello stream (legge e scarta) |
|
Controlla se c'è ancora qualcosa nello stream |
|
Chiude il flusso |
I metodi sono molto simili a quelli della InputStream
classe, anche se ci sono lievi differenze.
int read()
metodo
Questo metodo ne legge uno char
dal flusso e lo restituisce. Il char
tipo viene ampliato in un int
, ma i primi due byte del risultato sono sempre zero.
int read(char[] buffer)
metodo
Questa è la seconda variante del read()
metodo. Ti consente di leggere un array di caratteri da un Reader
tutto in una volta. L'array che memorizzerà i caratteri deve essere passato come argomento. Il metodo restituisce un numero, il numero di caratteri effettivamente letti.
skip(long n)
metodo
Questo metodo consente di saltare i primi n caratteri dell'oggetto Reader
. Funziona esattamente come il metodo analogo della InputStream
classe. Restituisce il numero di caratteri effettivamente ignorati.
boolean ready()
metodo
Restituisce true
se sono presenti byte non letti nel flusso.
void close()
metodo
Il close()
metodo chiude il flusso di dati e rilascia le risorse esterne ad esso associate. Una volta chiuso un flusso, non è più possibile leggere altri dati da esso.
Per confronto, scriviamo un programma che copia un file di testo:
Codice | Nota |
---|---|
|
Reader per la lettura da un file Writer per la scrittura in un file Buffer in cui leggeremo i dati Finché ci sono dati nello stream Leggere i dati in un buffer Scrivere i dati dal buffer nel secondo stream |
GO TO FULL VERSION