Olá! Na palestra de hoje continuaremos a conversa sobre fluxos de entrada e saída em Java, ou Java I/O (“entrada-saída”), para abreviar. Esta não é a primeira palestra sobre este tópico e não será a última :) Acontece que Java como linguagem oferece muitas oportunidades para trabalhar com entrada/saída. Existem muitas classes que implementam essa funcionalidade, então nós as dividimos em várias palestras para que você não fique confuso no início :) Nas palestras anteriores abordamos BufferedReader , bem como as classes abstratas InputStream & OutputStream e várias descendentes. Hoje veremos 3 novas classes: FileInputStream , FileOutputStream e BufferedInputStream .
Classe FileOutputStream
O objetivo principal da classe FileOutputStream é gravar bytes em um arquivo. Nada complicado :) FileOutputStream é uma das implementações da classe abstrata OutputStream . No construtor, os objetos desta classe seguem o caminho para o arquivo de destino (no qual os bytes precisam ser gravados) ou um objeto da classeFile
. Vejamos os dois exemplos:
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 JavaRush - the best site for those who want to become a programmer!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Ao criar um objeto, File
especificamos no construtor o caminho onde ele deve estar localizado. Não há necessidade de criá-lo antecipadamente: se não existir, o próprio programa o criará. Você pode fazer isso sem criar um objeto extra e apenas passar uma string com o endereço:
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 JavaRush - the best site for those who want to become a programmer!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
O resultado em ambos os casos será o mesmo. Podemos abrir nosso arquivo e ver lá:
Hello! Добро пожаловать на JavaRush — лучший сайт для тех, кто хочет стать программистом!
No entanto, há uma ressalva aqui. Tente executar o código do exemplo acima várias vezes seguidas e, em seguida, observe o arquivo e responda à pergunta: quantas linhas você vê escritas nele? Apenas um. Mas você executou o código várias vezes. Porém, acontece que os dados foram sobrescritos todas as vezes, substituindo os antigos. E se não estivermos satisfeitos com isso e precisarmos de gravação sequencial? E se quisermos escrever nossa saudação em um arquivo três vezes seguidas? Tudo é simples aqui. Como a linguagem em si não pode saber que tipo de comportamento precisamos em cada caso, FileOutputStream
você pode passar um parâmetro adicional para o construtor - boolean append
. Se o seu valor for true , os dados serão gravados no final do arquivo. Se false (e o valor padrão for false ), os dados antigos serão apagados e os novos dados serão gravados. Vamos testar e executar 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 JavaRush - the best site for those who want to become a programmer!\r\n";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Resultado no arquivo:
Hello! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!
Hello! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!
Hello! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!
Outra coisa! Lembre-se desse recurso ao usar classes de E/S. Ao mesmo tempo, eu tinha que ficar horas sentado em tarefas para entender para onde iam meus dados antigos dos arquivos :) E claro, como no caso de outras classes de I/O, não se esqueça de liberar recursos por meio do close()
.
Classe FileInputStream
A classe temFileInputStream
o propósito oposto – ler bytes de um arquivo. Assim como FileOutputStream
herda OutputStream
, esta classe deriva da classe abstrata InputStream
. Vamos escrever várias linhas de texto em nosso texto “ 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»
Isto é o que acontece com a implementação da leitura de dados de um arquivo usando 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 enviamos para o console. E aqui está o resultado no console:
So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters
Classe BufferedInputStream
Acho que, dado o conhecimento de palestras anteriores, você pode facilmente dizer por que a aula é necessáriaBufferedInputStream
e quais vantagens ela tem FileInputStream
:) Já conhecemos streams em buffer, então tente adivinhar (ou lembre-se) antes de continuar lendo :) Streams em buffer são necessários principalmente para otimizar E/S. Acessar uma fonte de dados, como ler um arquivo, é uma operação que exige muito desempenho. E acessar o arquivo para ler um byte de cada vez é um desperdício. Portanto, BufferedInputStream
ele 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 funcionamento do programa reduzindo o número de acessos ao arquivo. Vamos ver como é:
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 objeto BufferedInputStream
. Ele aceita um objeto ou qualquer um de seus sucessores como input InputStream
, então o anterior FileInputStream
servirá. Leva o tamanho do buffer em bytes como um parâmetro adicional. Agora, graças a isso, os dados do arquivo serão lidos não um byte por vez, mas 200 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 com vários megabytes de tamanho e comparar quanto tempo leva para lê-lo e enviá-lo para o console em milissegundos usando FileInputStream
e BufferedInputStream
. Aqui estão os dois exemplos de código:
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 em meu computador, FileInputStream
ele fez o trabalho em aproximadamente 3.500 milissegundos, mas aqui BufferedInputStream
ele fez o trabalho em aproximadamente 1.700 milissegundos. Como você pode ver, o fluxo em buffer otimizou o desempenho do programa em 2 vezes! :) Continuaremos estudando aulas de I/O - até breve!
GO TO FULL VERSION