"Oi! Na lição de hoje, continuaremos nossa conversa sobre fluxos de entrada e saída em Java ( Java I/O ). Esta não é a primeira lição sobre este tópico e certamente não será a última
:) acontecer, a linguagem Java fornece muitas maneiras de trabalhar com E/S. Existem algumas classes que implementam essa funcionalidade, então as dividimos em várias lições — para que você não fique confuso desde o início :) No passado lições, abordamos
Veja como é ler dados de um arquivo usando

BufferedReader
, bem como as classes abstratas InputStream
e OutputStream
e vários descendentes. Hoje vamos considerar 3 novas classes: FileInputStream
, FileOutputStream
, e BufferedInputStream
.
A classe FileOutputStream
O principal objetivo daFileOutputStream
classe é gravar bytes em um arquivo. Nada complicado :) FileOutputStream
é uma das implementações da OutputStream
classe abstrata. No construtor, os objetos dessa classe levam o caminho para o arquivo de destino (onde os bytes devem ser gravados) ou um File
objeto. Examinaremos exemplos de cada um:
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Username\\Desktop\\test.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Ao criar o File
objeto, passamos o caminho desejado para o construtor. Não precisamos criá-lo com antecedência: se não existir, o programa o criará. Você também pode passar sem criar um objeto extra, simplesmente passando uma string com o caminho:
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt");
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
O resultado em ambos os casos será o mesmo. Podemos abrir nosso arquivo e ver o seguinte:
Hi! Welcome to CodeGym — The best site for would-be programmers!
Mas há uma nuance aqui. Tente executar o código do exemplo acima várias vezes seguidas. Em seguida, olhe no arquivo e responda a esta pergunta: quantas linhas ele tem? Apenas um. Mas você executou o código várias vezes. Acontece que os dados são substituídos todas as vezes - o antigo é substituído pelo novo. O que fazemos se isso não nos convém e precisamos escrever sequencialmente no arquivo? E se quisermos escrever nossa saudação em um arquivo três vezes seguidas? É tudo muito simples. Como a linguagem não pode saber qual comportamento precisamos em cada caso, o FileOutputStream
construtor pode receber um parâmetro adicional —boolean append
. Se seu valor for true, os dados serão gravados no final do arquivo. Se for falso (e por padrão é falso), todos os dados antigos serão apagados e substituídos por novos dados. Vamos verificar isso executando nosso código modificado três vezes:
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt", true);
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!\r\n";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Conteúdo do arquivo:
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
Agora isso é diferente! Não se esqueça desse recurso ao usar classes de E/S. Houve um tempo em que eu passava horas em tarefas, quebrando a cabeça por horas, tentando entender como meus dados estavam desaparecendo dos arquivos :) E claro, assim como em outras classes de I/O, não se esqueça de usar o close()
método para liberar recursos.
A classe FileInputStream
OFileInputStream
tem o propósito oposto - ler bytes de um arquivo. Assim como FileOutputStream
a herança OutputStream
, esta classe deriva da InputStream
classe abstrata. Escreveremos algumas linhas de texto em nosso arquivo " test.txt ":
"So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters"

FileInputStream
:
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");
int i;
while((i=fileInputStream.read())!= -1){
System.out.print((char)i);
}
}
}
Lemos um byte do arquivo, convertemos os bytes lidos em caracteres e os exibimos no console. E aqui está a saída do console:
So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters
A classe BufferedInputStream
Eu acho que, dado o conhecimento das lições anteriores, você pode facilmente dizer por que precisamos daBufferedInputStream
aula e quais vantagens ela tem em comparação com FileInputStream
:) Já encontramos fluxos em buffer, então tente adivinhar (ou lembre-se) antes de continuar lendo :) Fluxos com buffer são necessários principalmente para otimizar a E/S. Acessar uma fonte de dados, como a leitura de um arquivo, é uma operação cara em termos de desempenho. E acessar um arquivo para ler cada byte é um desperdício. É por isso que BufferedInputStream
lê os dados não um byte por vez, mas em blocos e os armazena temporariamente em um buffer especial. Isso nos permite otimizar o programa reduzindo o número de vezes que acessamos o arquivo. Vamos ver como isso se parece:
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 200);
int i;
while((i = bufferedInputStream.read())!= -1){
System.out.print((char)i);
}
}
}
Aqui criamos um BufferedInputStream
objeto. Seu construtor pega uma instância da InputStream
classe ou qualquer um de seus descendentes, então FileInputStream
o fará. Como argumento adicional, considera o tamanho do buffer em bytes. Graças a este argumento, os dados agora serão lidos do arquivo não um byte por vez, mas 200 bytes por vez! Imagine o quanto reduzimos o número de acessos a arquivos. Para comparar o desempenho, você pode pegar um arquivo de texto grande (vários megabytes de texto) e comparar quanto tempo leva em milissegundos para ler e enviar para o console usando FileInputStream
e BufferedInputStream
. Aqui está o código que demonstra ambas as opções:
public class Main {
public static void main(String[] args) throws IOException {
Date date = new Date();
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\textBook.rtf");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int i;
while((i = bufferedInputStream.read())!= -1){
System.out.print((char)i);
}
Date date1 = new Date();
System.out.println((date1.getTime() - date.getTime()));
}
}
public class Main {
public static void main(String[] args) throws IOException {
Date date = new Date();
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\26951280.rtf");
int i;
while((i = fileInputStream.read())!= -1){
System.out.print((char)i);
}
Date date1 = new Date();
System.out.println((date1.getTime() - date.getTime()));
}
}
Ao ler um arquivo de 1,5 MB no meu computador, FileInputStream
concluí o trabalho em ~ 3.500 milissegundos, mas BufferedInputStream
o gerenciei em ~ 1.700 milissegundos. Como você pode ver, o fluxo em buffer otimizou o trabalho, cortando-o pela metade! :) Continuaremos a estudar as aulas de I/O — até breve!
GO TO FULL VERSION