1. OutputStreamaula

Recentemente, exploramos os fluxos de entrada. É hora de falar sobre fluxos de saída.

A OutputStreamclasse é a classe pai de todas as classes que suportam a saída de bytes. Esta é uma classe abstrata que não faz nada sozinha, mas tem classes descendentes para cada ocasião.

Parece extremamente complicado. Simplificando, essa classe opera em bytes e não, por exemplo, em caracteres ou outros tipos de dados. E o fato de ser abstrato significa que normalmente não o usamos, mas sim uma de suas classes descendentes. Por exemplo, FileOutputStreame assim por diante.

Mas voltando à OutputStreamaula. Esta classe tem métodos que todas as suas classes descendentes devem implementar. Aqui estão os principais:

Métodos Descrição
void write(int b)
Grava um byte (não um int) no fluxo.
void write(byte[] buffer)
Grava uma matriz de bytes no fluxo
void write(byte[] buffer, off, len)
Grava parte de uma matriz de bytes no fluxo
void flush()
Grava todos os dados armazenados no buffer no fluxo
void close()
Fecha o fluxo

Quando você cria um objeto de uma classe que herda InputStream, geralmente especifica um objeto de origem do qual InputStreamlê os dados. Quando você cria um objeto de uma classe que herda OutputStream, geralmente também especifica o objeto ou fluxo de destino no qual os dados serão gravados.

Vamos passar rapidamente por todos os métodos da OutputStreamclasse:

write(int b)método

Esse método grava um byte (não um int) no fluxo de saída. O valor passado é convertido em um byte e os três primeiros bytes do int são descartados.

write(byte[] buffer)método

Grava a matriz de bytes fornecida no fluxo de saída. É isso.

write(byte[] buffer, int offset, int length)método

Grava uma parte da matriz de bytes transmitida no fluxo de saída. A variável offset indica o índice do primeiro elemento do array, e lengthé o comprimento do subconjunto a ser escrito.

flush()método

O flush()método é usado para forçar qualquer dado potencialmente armazenado em buffer no fluxo atual a ser gravado no fluxo de destino. Isso é relevante ao usar armazenamento em buffer e/ou vários objetos de fluxo organizados em uma cadeia.

close()método

Grava todos os dados não gravados no objeto de destino. O close()método não precisa ser chamado se você usar um try-with-resourcesbloco.

Exemplo de cópia de um arquivo

Código Observação
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreampara ler de um arquivo
OutputStreampara gravar em um arquivo

Buffer no qual leremos os dados
Enquanto houver dados no fluxo

Ler dados no buffer
Gravar os dados do buffer no segundo fluxo

2. Writerclasse

A Writerclasse é exatamente igual à OutputStreamclasse, mas apenas uma diferença mais uma vez: ela trabalha com caracteres ( char) em vez de bytes.

Esta é uma classe abstrata: você não pode criar objetos da Writerclasse. Seu principal objetivo é ser uma classe pai comum para centenas de classes descendentes e fornecer a elas métodos comuns para trabalhar com fluxos de caracteres.

Métodos da Writerclasse (e todas as suas classes descendentes):

Métodos Descrição
void write(int b)
Grava um caractere (não um int) no fluxo.
void write(char[] buffer)
Grava uma matriz de caracteres no fluxo
void write(char[] buffer, off, len)
Grava parte de uma matriz de caracteres no fluxo
void write(String str)
Grava uma string no stream
void write(String str, off, len)
Grava parte de uma string no stream
void flush()
Grava todos os dados armazenados no buffer no fluxo
void close()
Fecha o fluxo

Os métodos são muito semelhantes aos métodos da OutputStreamclasse, mas trabalham com caracteres em vez de bytes.

Descrição dos métodos:

write(int b)método

Este método grava um único caractere ( char— não um int) no fluxo de saída. O valor passado é convertido em um chare os dois primeiros bytes são descartados.

write(char[] buffer)método

Grava a matriz de caracteres fornecida no fluxo de saída.

write(char[] buffer, int offset, int length)método

Grava uma parte da matriz de caracteres transmitida no fluxo de saída. A offsetvariável indica o índice do primeiro elemento da matriz e lengthé o comprimento do subconjunto a ser escrito.

write(String str)método

Grava a string fornecida no fluxo de saída.

write(String str, int offset, int length)método

Grava uma parte da string fornecida no fluxo de saída: a string é convertida em uma matriz de caracteres. A offsetvariável indica o índice do primeiro elemento da matriz e lengthé o comprimento do subconjunto a ser escrito.

flush()método

O flush()método é usado para forçar qualquer dado potencialmente armazenado em buffer no fluxo atual a ser gravado no fluxo de destino. Isso é relevante ao usar armazenamento em buffer e/ou vários objetos de fluxo organizados em uma cadeia.

close()método

Grava todos os dados não gravados no objeto de destino. O close()método não precisa ser chamado se você usar um try-with-resourcesbloco.

Exemplo de um programa que copia um arquivo de texto:

Código Observação
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerpara ler de um arquivo
Writerpara gravar em um arquivo

Buffer no qual leremos os dados
Enquanto houver dados no fluxo

Ler dados em um buffer
Gravar os dados do buffer no segundo fluxo

StringWriteraula

Existe outra classe interessante que herda a Writerclasse: ela se chama StringWriter. Ele contém uma string mutável — um StringBufferobjeto. E toda vez que você "escreve" algo no StringWriterobjeto, o texto é simplesmente adicionado ao seu buffer interno.

Exemplo:

Código Observação
StringWriter writer = new StringWriter();
writer.write("Hello");
writer.write(String.valueOf(123));

String result = writer.toString();
Um fluxo de caracteres de destino ( StringWriter) é criado
Uma string é gravada no buffer dentro do StringWriter
Uma string é gravada no buffer dentro do StringWriter

Convertendo o conteúdo de um objeto em uma string

Nesse caso, a StringWriterclasse é essencialmente um wrapper sobre a StringBufferclasse, mas a StringWriterclasse é descendente da Writerclasse de fluxo e pode ser usada em cadeias de objetos de fluxo. Esta é uma propriedade bastante útil na prática.



3. PrintStreamclasse

As classes de fluxo de saída também podem ser colocadas em uma cadeia com fluxos intermediários que gravam dados no fluxo de destino passado para eles. A visão geral da interação desses fluxos é assim:

Classe PrintStream

O mais interessante e versátil de todos os fluxos de saída intermediários é PrintStream. Possui dezenas de métodos e até 12 construtores.

A PrintStreamclasse herda a FilterOutputStreamclasse, que herda OutputStream. Isso significa que a PrintStreamclasse tem todos os métodos das classes pai além de seus próprios métodos . Aqui estão os mais interessantes:

Métodos Descrição
void print(obj)
Converte o objeto passado em uma string e a envia para o fluxo de destino.
void println(obj)
Converte o objeto passado em uma string e a envia para o fluxo de destino. Adiciona uma quebra de linha no final
void println()
Emite um caractere de quebra de linha para o fluxo de destino
PrintStream format(String format, args...)
Constrói e gera uma string com base na string de formato e nos argumentos passados; semelhante ao String.format()método

E onde estão essas dezenas de métodos, você pergunta?

Bem, ele tem muitas variantes dos métodos print()e println()com diferentes parâmetros. Eles podem ser resumidos nesta tabela.

Não vamos nos aprofundar nesses métodos, porque você já os conhece bem. Você consegue adivinhar onde quero chegar?

Lembra System.out.println()? Mas pode ser escrito em duas linhas:

Código Saída do console
PrintStream stream = System.out;
stream.println("Hello!");
Hello!

Nosso comando favorito é uma chamada para o método na variável estática da classe. E o tipo dessa variável é .System.out.println()println()outSystemPrintStream

Em muitos níveis do CodeGym, e em quase todas as tarefas, você chamou métodos da PrintStreamclasse sem saber!

Uso pratico

Java tem uma classe interessante chamada ByteArrayOutputStream, que é uma matriz de bytes dinamicamente crescente que herda OutputStream.

Um ByteArrayOutputStreamobjeto e PrintStreamum objeto podem ser encadeados assim:

Código Descrição
ByteArrayOutputStream baos = new ByteArrayOutputStream();

try(PrintStream stream = new PrintStream(baos))
{
   stream.println("Hello");
   stream.println(123);
}

String result = baos.toString();

System.out.println(result);
Criar um buffer de gravação na memória

Envolver o buffer em um PrintStreamobjeto

Gravar dados no console



Converter o array em uma string!

Saída do console:
Hello!
123