"Ciao, Amigo! Oggi faremo conoscenza con i flussi di input/output . Abbiamo scelto questo argomento un paio di giorni fa, ma oggi lo esploreremo a fondo. I flussi di input/output sono divisi in 4 categorie:"

1) I flussi sono divisi in base alla loro direzione: flussi di input e flussi di output

2) Gli stream sono divisi in base al loro tipo di dati: quelli che funzionano con i byte e quelli che funzionano con i caratteri .

Qui queste divisioni sono rappresentate in una tabella:

Flusso di input Flusso di uscita
Funziona con i byte InputStream OutputStream
Funziona con i personaggi Lettore scrittore

Se un oggetto implementa l' interfaccia InputStream , supporta la capacità di leggere sequenzialmente i byte da esso.

Se un oggetto implementa l' interfaccia OutputStream , allora supporta la capacità di scrivere sequenzialmente byte su di esso.

Se un oggetto implementa l' interfaccia Reader , supporta la capacità di leggere in sequenza caratteri (caratteri) da esso.

Se un oggetto implementa l' interfaccia Writer , supporta la capacità di scrivere in sequenza caratteri (chars) su di esso.

Flussi di ingresso/uscita - 1

Un flusso di output è come una stampante. Possiamo inviare i documenti alla stampante. Possiamo inviare i dati a un flusso di output.

Da parte sua, un flusso di input può essere paragonato a uno scanner o forse a una presa elettrica. Con uno scanner, possiamo portare i documenti sul nostro computer. Oppure possiamo collegarci a una presa elettrica e ricevere elettricità da essa. Possiamo ricevere dati da un flusso di input.

"Dove vengono usati?"

"Queste classi sono utilizzate ovunque in Java. Il nostro amico familiare System.in è una variabile InputStream statica denominata nella classe System ."

"Sul serio?! Quindi per tutto questo tempo ho usato un InputStream e non me ne sono nemmeno reso conto. Anche System.out è uno stream?"

"Sì, System.out è una variabile statica PrintStream (discendente di OutputStream ) nella classe System."

"Vuoi dirmi che ho sempre usato i flussi e non lo sapevo nemmeno?"

"Sì, e questo ci dice solo quanto siano convenienti questi flussi. Basta prenderne uno e usarlo."

"Ma non si può dire lo stesso di System.in. Dovevamo costantemente aggiungere BufferedReader o InputStreamReader."

"È vero. Ma c'erano anche delle ragioni per questo."

Esistono molti tipi di dati e molti modi per lavorarci. Quindi il numero di classi I/O standard è cresciuto molto rapidamente, sebbene facessero tutto quasi allo stesso modo. Per evitare questa complessità, gli sviluppatori Java hanno utilizzato il principio dell'astrazione e hanno diviso le classi in tante piccole parti.

Ma puoi collegare queste parti in modo coerente e ottenere funzionalità molto complesse, se ne hai bisogno. Guarda questo esempio:

Invia una stringa alla console
System.out.println("Hello");
Memorizza il flusso di output della console in una variabile separata.
Invia una stringa al flusso.
PrintStream console = System.out;
console.println("Hello");
Crea un array di byte dinamico (in espansione) in memoria.
Collegalo a un nuovo flusso di output (oggetto PrintStream).
Invia una stringa al flusso.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintStream console = new PrintStream(stream);
console.println("Hello");

"Onestamente, questo è come un set Lego. Solo che non mi è chiaro cosa stia facendo questo codice."

"Non preoccuparti di questo per ora. Ogni cosa a suo tempo."

Questo è ciò che voglio che tu ricordi: se una classe implementa l'interfaccia OutputStream, puoi scriverci dei byte. Quasi esattamente come invii i dati alla console. Quello che ne fa sono affari suoi. Con il nostro "kit Lego", non ci interessa lo scopo di ogni singola parte. Ci preoccupiamo del fatto che l'ampia selezione di parti ci consenta di costruire cose così interessanti.

"Va bene. Allora da dove cominciamo?"