"Olá, amigo! Hoje vamos nos familiarizar com fluxos de entrada/saída . Escolhemos este tópico alguns dias atrás, mas hoje vamos explorá-lo completamente. Os fluxos de entrada/saída são divididos em 4 categorias:"

1) Os fluxos são divididos de acordo com sua direção: fluxos de entrada e fluxos de saída

2) Os streams são divididos de acordo com seu tipo de dados: os que trabalham com bytes e os que trabalham com caracteres .

Aqui essas divisões são representadas em uma tabela:

Fluxo de entrada fluxo de saída
Funciona com bytes InputStream OutputStream
Funciona com personagens Leitor Escritor

Se um objeto implementa a interface InputStream , ele oferece suporte à capacidade de ler bytes sequencialmente.

Se um objeto implementa a interface OutputStream , ele oferece suporte à capacidade de gravar bytes sequencialmente.

Se um objeto implementa a interface Reader , ele suporta a capacidade de ler sequencialmente os caracteres (caracteres) dele.

Se um objeto implementa a interface Writer , ele oferece suporte à capacidade de gravar caracteres sequencialmente (caracteres) nele.

Fluxos de entrada/saída - 1

Um fluxo de saída é como uma impressora. Podemos enviar documentos para a impressora. Podemos enviar dados para um fluxo de saída.

De sua parte, um fluxo de entrada pode ser comparado a um scanner ou talvez a uma tomada elétrica. Com um scanner, podemos trazer documentos para o nosso computador. Ou podemos conectar a uma tomada elétrica e receber eletricidade dela. Podemos receber dados de um fluxo de entrada.

"Onde eles são usados?"

"Essas classes são usadas em todos os lugares em Java. Nosso conhecido System.in é uma variável estática InputStream nomeada na classe System ."

"Sério?! Então, todo esse tempo eu tenho usado um InputStream e nem percebi. System.out também é um stream?"

"Sim, System.out é uma variável estática PrintStream (um descendente de OutputStream ) na classe System."

"Quer dizer que sempre usei streams e nem sabia?"

"Sim, e isso apenas nos diz como esses fluxos são convenientes. Basta pegar um e usá-lo."

"Mas você não pode dizer isso sobre System.in. Constantemente tivemos que adicionar BufferedReader ou InputStreamReader a ele."

"Isso é verdade. Mas também havia razões para isso."

Existem muitos tipos de dados e muitas maneiras de trabalhar com eles. Assim, o número de classes de E/S padrão cresceu muito rapidamente, embora fizessem tudo quase da mesma maneira. Para evitar essa complexidade, os desenvolvedores Java usaram o princípio da abstração e dividiram as classes em muitas partes pequenas.

Mas você pode conectar essas partes de maneira coerente e obter uma funcionalidade muito complexa, se precisar. Veja este exemplo:

Emita uma string para o console
System.out.println("Hello");
Armazene o fluxo de saída do console em uma variável separada.
Emita uma string para o fluxo.
PrintStream console = System.out;
console.println("Hello");
Crie uma matriz de bytes dinâmica (em expansão) na memória.
Conecte-o a um novo fluxo de saída (objeto PrintStream).
Emita uma string para o fluxo.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintStream console = new PrintStream(stream);
console.println("Hello");

"Honestamente, isso é como um conjunto de Lego. Só que não está claro para mim o que esse código está fazendo."

"Não se preocupe com isso por enquanto. Tudo no seu devido tempo."

Isto é o que eu quero que você lembre: se uma classe implementa a interface OutputStream, você pode gravar bytes nela. Quase exatamente como você envia dados para o console. O que ela faz com isso é problema dela. Com o nosso "kit Lego", não nos preocupamos com a finalidade de cada peça individual. Nós nos preocupamos com o fato de que a grande variedade de peças nos permite construir coisas tão legais.

"Ok. Então por onde começamos?"