Black Friday 20% de desconto
Tá acabando

0

dias

00

hrs

00

min

00

seg

Primeiras aulas do curso Java e java.io: Streams, Reader e Writers

Java e java.io: Streams, Reader e Writers

Leitura com java.io - Introdução

Olá! Bem-vindo ao curso de Java Parte 7: Trabalhando com java.io.

Trata-se de uma ferramenta fundamental para qualquer desenvolvedor que deseje trabalhar com as classes padrões Java.

Neste curso, focaremos na leitura e escrita de arquivos binários e textuais. Falaremos sobre as principais classes, como utiliza-las, e como combina-las.

O pacote java.io é repleto de conceitos de pacotes e classes interessantes.

Veremos ainda os problemas mais comuns que surgem ao trabalharmos com a codificação de caracteres, por exemplo quando algum caractere especial não é interpretado corretamente.

Estabeleceremos a entrada e saída de fluxos diferentes, do console, para a rede, para o arquivo, citando apenas alguns exemplos. Estudaremos o padrão Java de transformação de um objeto em um fluxo, e vice-versa.

Temos muito conteúdo, convido você a seguir conosco pelas próximas aulas!

Leitura com java.io - Estabelecendo a entrada

Caso você queira baixar o zip com o arquivo lorem.txt, clique aqui.


Nesta aula, daremos início ao nosso curso focado no pacote java.io.

Atualmente, nenhuma aplicação funciona isoladamente e não receba ou envie dados. Casos em que isto não aconteça são exceções raras.

Temos, em geral, um fluxo de dados de entrada e outro de saída. Por exemplo, aqueles que assistem aos vídeos a partir do navegador, conseguem fazê-lo porque eles vêm de um servidor, ou seja, um fluxo (ou streaming) de informações.

O mesmo é verdade para o mobile. Ainda que seja feito o download prévio, o aplicativo da Alura lê o arquivo no HD para que o usuário possa assisti-lo.

Ou seja, podemos concluir que sempre há uma entrada, e esta sempre pode variar. Pode ser um arquivo, a rede, ou ainda um teclado. Estes são os tipos de entrada concreta.

Para a aplicação, isto não tem grande relevância, já que de qualquer forma todos representam uma entrada.

O mesmo é válido para a saída, a aplicação mobile retornará dados para a Alura, por exemplo, no momento em que um usuário conclui um vídeo ou exercício. O fluxo de saída concreto é variável, pode ser que o usuário decida gravar um arquivo ou que haja um retorno por meio da rede, como é o caso da Alura, ou ainda, podemos ter um retorno no console que é aquele que vemos ao executar um programa no Eclipse, por exemplo.

Ainda que o tipo de fluxo varie, para aplicação, é importante que haja uma saída. Isto é válido para qualquer aplicação, ou pelo menos para a vasta maioria delas.

Passaremos a trabalhar com as classes do java.io para modelarmos a estrutura de entrada e saída que foi mencionada acima. Focaremos, primeiro, no fluxo de entrada, em particular, no arquivo. Estabeleceremos uma entrada a partir de um arquivo.

Abriremos o Eclipse. Estamos utilizando o Oxygen na Versão 2.

Os projetos que são exibidos no menu lateral esquerdo estão disponíveis para download, mas se preferir, este pode ser feito posteriormente já que trabalharemos ainda com o bytebank-herdado-conta.

Neste momento, para darmos continuidade, criaremos um novo projeto. Para isso, clicaremos com o botão direito sobre a barra lateral esquerda, onde temos o menu de exploração de arquivos, e selecionaremos a opção "New > Java Project".

Será um projeto Java padrão, onde utilizaremos a Java SE 10.0.0 - os recursos com os quais trabalhamos funcionam com versões anteriores do Java, portanto, não precisamos nos preocupar com esta questão. O nome do nosso projeto será java-io, e nele faremos os nossos testes com a entrada e saída.

Em seguida, podemos partir para a criação da nossa primeira classe.

Como queremos trabalhar com a entrada a partir de um arquivo, primeiro, temos que ter este arquivo. Como exemplo, utilizaremos um arquivo em formato .txt, no qual há um texto de exemplo em Lorem Ipsum, ele está disponível para download mas pode ser substituído por qualquer outro do mesmo formato, desde que tenha um conteúdo.

O arquivo deve ser inserido na raiz do projeto, ou seja, na própria pasta java.io, e não na pasta src.

Criaremos uma classe, clicando com o botão direito do mouse sobre a pasta src, selecionaremos a opção "New > Class". Ela será inserida no pacote br.com.alura.java.io.teste e terá o nome TesteLeitura, já com o método main:

package br.com.alura.java.io.teste;

public class TesteLeitura {

        public static void main(String[] args) {
                //TODO Auto-generated method stub
        }
}

Apagaremos a linha de código gerada automaticamente pelo Java.

Nosso objetivo é estabelecer um fluxo de entrada com um arquivo. Já que em Java trabalhamos com a língua inglesa, precisamos traduzir certos termos, arquivo por exemplo, é "file", entrada é "input", e fluxo é "stream", resultado em um FileInputStream:

package br.com.alura.java.io.teste;

public class TesteLeitura {

        public static void main(String[] args) {

                //Fluxo de Entrada com Arquivo

                FileInputStream
        }
}

Nossa variável se chamará fis, e criaremos um objeto do tipo FileInputStream(). Neste ponto, ele reconhecerá o pacote java.io. Ao confirmarmos, será a importação ocorrerá automaticamente:

package br.com.alura.java.io.teste;

import java.io.FileInputStream;

public class TesteLeitura {

        public static void main(String[] args) {

                //Fluxo de Entrada com Arquivo

                FileInputStream fis = new FileInputStream(file)
        }
}

A seguir, veremos os diferentes tipos de construtores. O primeiro que aparece na lista de sugestões apresentada pelo Eclipse, recebe um arquivo File, entretanto, não é esse que utilizaremos, uma vez que nos é disponibilizado algo ainda mais simples, que é o construtor String name, representando o nome do arquivo com o qual desejamos trabalhar.

Para o utilizarmos, basta escrevermos o nome do arquivo como uma String, no caso, nosso arquivo é o lorem.txt:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) {

                //Fluxo de Entrada com Arquivo

                FileInputStream fis = new FileInputStream("lorem.txt");
        }
}

Contudo, o código ainda não compila. O Eclipse nos informa que, para que isso aconteça, precisamos fazer ainda um tratamento de exceção.

Como sabemos, há dois tipos de exceção, checked e unchecked, o java.io está repleto de exceções checked .

O Java não é capaz de garantir que o desenvolvedor realmente inseriu o arquivo na raiz do projeto, por isso, o código está passível de falhas. Precisamos alertar sobre esta falha, e o modo pelo qual fazemos isso é a exceção do tipo checked.

Neste caso, criaremos um throws de FileNotFoundException:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws FileNotFoundException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
        }
}

Utilizando a variável fis, podemos utilizar uma série de métodos, dentre eles, está o read(). O seu retorno é do tipo int, ou seja, um número. Isso indica que ele é capaz de ler os bytes, o que não é interessante para nós, não queremos as informações de bytes e binários, mas sim os caracteres.

Entretanto, nos parece que o FileInputStream não é capaz de realizar isto que desejamos. Para isso, teremos de utilizar uma outra classe.

Há uma classe capaz de transformar um int em caracteres, que se chama InputStreamReader.

A ideia é que ela é capaz de ler um FileInputStream.

Criaremos uma variável isr, com um objeto do tipo InputStramReader(), que receberá em seu construtor um fis:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws FileNotFoundException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
        }
}

A variável isr nos permite utilizar uma outra variedade de métodos, além do método read() citado acima, há um segundo, que recebe como parâmetro um array de caracteres. Ele também nos retorna um int, entretanto, neste caso ele corresponde ao número de caracteres que foram lidos.

Neste caso, conseguimos transformar bits e bytes em caracteres, mas ainda não é a melhor solução para nosso problema.

A ideia é que sejamos capazes de ler as linhas inteiras do arquivo de texto, para isso, temos que "guardar" cada um dos caracteres, até sermos capazes de completar uma linha, e assim por diante.

Para esta tarefa, há o que chamamos de BufferedReader. Criaremos um em nosso código:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws FileNotFoundException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br =  new BufferedReader(in);
        }
}

Como parâmetro, ele receber um outro reader, no caso, nosso InputStreamReaderse qualifica como tal, por isso, como passaremos o isr:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws FileNotFoundException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br = new BufferedReader(isr);
        }
}

Primeiro, criamos o fluxo concreto com o arquivo, mas ainda binário, em seguida, conseguimos transforma-los em caracteres, mas apenas a contabilização, por fim, com o BufferedReader, podemos utilizar o método readLine(), que nos permite ler linha a linha.

Este método nos retorna uma String, que representa a linha:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws FileNotFoundException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br = new BufferedReader(isr);

                String linha = br.readLine();
        }
}

O Eclipse sinaliza que o programa ainda não está funcionando, para isso, teremos que fazer um novo tratamento, ou IOException.

Ao trabalharmos com java.io é necessário dominarmos dois tipos principais de exceção, a primeira é a FileNotFoundException, que já vimos, e a segunda é a IOException.

Com a tecla "Ctrl" pressionada, clicaremos sobre FileNotFoundException e abriremos esta classe. Veremos que ela estende a IOException:

//Código omitido

public class FileNotFoundException extends IOException {

//Código omitido

Portanto, a FileNotFoundException é uma IOException, esta por sua vez, é uma exceção, já que estende Exception. Por isso, em vez de utilizarmos a exceção mais específica, utilizaremos o tipo mais genérico:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws IOException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br =  new BufferedReader(isr);

                String linha = br.readLine();
        }
}

Lembrando que, como estamos utilizando uma nova classe, precisamos importá-la.

Já sabemos ler uma linha, mas precisamos ler as demais. Por enquanto, imprimiremos apenas esta primeira linha, para criarmos uma saída, representada no caso por out:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws IOException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br = new 
BufferedReader(isr);

                String linha = br.readLine();

                System.out.println(linha);
        }
}

O compilador indica que estabelecemos uma entrada em BufferedReader, mas não uma saída, assim, fecharemos com o br.close():

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws IOException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br = new BufferedReader(isr);

                String linha = br.readLine();

                System.out.println(linha);

                br.close();
        }
}

Isso faz com que tanto o FileInputStream quanto o InputStreamReader sejam fechados automaticamente. Por isso não há necessidade de os fecharmos individualmente.

Salvaremos todo o código. Executaremos, e temos o seguinte resultado no console:

Lorem ipsum dolor sit amet, consectetur elit, sed do eiusmod

Funcionou, imprimimos a primeira linha do texto do nosso arquivo.

Adiante, veremos como podemos melhorar este código. Até lá!

Leitura com java.io - InputStream e Reader

Anteriormente, conseguimos estabelecer uma entrada e escrevemos código capaz de ler a primeira linha de nosso arquivo lorem.txt.

Nas seguintes linhas de código:

public class TesteLeitura {

//Código omitido

//Fluxo de Entrada com Arquivo
FileInputStream fis = new FileImputStream("lorem.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);

}

Foi estabelecida a entrada com o arquivo e, além disso, melhoramos a leitura, já que nosso objetivo era traduzir a linha inteira. Para isso, foi necessário utilizarmos as classes InputStreamReader e BufferedReader. A primeira transforma bytes em caracteres, enquanto a segunda é responsável por unir os caracteres em uma linha em interpretá-los, linha a linha.

Temos a referência fis, que aponta para o objeto FileInputStream("lorem.txt"), e foi inserida como parâmetro no construtor InputStreamReader(fis).

O mesmo foi feito com a referência isr, que aponta para o objeto InputStreamReader(fis), e foi inserida como parâmetro no construtor BufferedReader(isr).

Na prática, isso significa que o FileInputStream é administrado por meio do InputStreamReader, este por sua vez, é administrado pelo BufferedReader, pois é passado no construtor.

Ao utilizarmos o método br.readLine(), pedimos primeiro ao BufferedLine, ele por sua vez faz o pedido ao InputStreamReader que, seguindo a ordem, pede ao FileStreamReader que faça a leitura dos dados do arquivo, que no caso é lorem.txt. Visualmente, temos algo como o desenho a seguir:

BufferedReader> InputStreamReader > FileInputStream > lorem.txt

Isso que fizemos é um padrão de projeto chamado decorator, ou seja, um objeto está decorando a funcionalidade de outro, sucessivamente. Em geral, o java.io é repleto de padrões de projeto.

Nosso objetivo seguinte será ler linha a linha do arquivo, até sua totalidade.

O método readLine() nos dá um retorno null quando não há mais nenhum conteúdo, portanto, criaremos um while, indicando que, enquanto a linha não for nula (null), teremos a impressão desta e leremos a próxima:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws IOException {

                //Fluxo de Entrada com Arquivo
                FileInputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br = BufferedReader(isr);

                String linha = br.readLine();

                while(linha != null) {
                        System.out.println(linha);
                        linha = br.readLine();
                }

                System.out.println(linha);

                br.close();
        }
}

Salvaremos e executaremos o código. Temos o seguinte resultado no console:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod 
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 
consequat. Duis aute irure dolor in reprehenderit in voluptate velit 
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat 
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim 
id est laborum.

Temos o texto impresso integralmente, indicando que o código funcionou!

Concluímos nosso primeiro objetivo, que era estabelecer uma entrada a partir de um arquivo, o fluxo de entrada, e uma saída para o console, representando o fluxo de saída.

Em seguida, trabalharemos dois conceitos que vemos muito presentes em nosso código, os termos Stream e Reader. Eles existem tanto para entrada quanto saída, mas por enquanto focaremos somente na entrada.

Primeiro, temos um aStream, capaz de ler bits e bytes, um "input stream of bytes". Em contrapartida, há o Reader, que também faz uma leitura, só que esta é focada nos caracteres, "reading character streams".

Se precisamos ler uma imagem ou um PDF, por exemplo, utilizamos sempre o Stream, já se trabalhamos com um arquivo de texto, devemos utilizar o Reader.

Ademais, há algo ainda mais geral que o FileInputStream, um conceito que representa o fluxo de dados binários, que é a classe (abstrata) InputStream.

No mundo Reader, vimos duas classes, a InputStreamReader e BufferedReader. O que ambas têm em comum é que são Readers, ou seja, compete à elas a leitura de caracteres. Assim, o Reader também é um conceito, uma classe abstrata, que tem estas duas classes como filhos concretos.

É fundamental compreendermos a existência destes dois mundos, dos Streams e Readers, focados na leitura dos dados.

No Eclipse, podemos visualizar a classe FileInputStream:

//Código omitido

public class FileInputStream extends InputStream {

//Código omitido

Vemos que ela estende InputStream. Ou seja, lembrando do conceito de polimorfismo, podemos utilizar este tipo mais genérico em nosso código, sem esquecer de importar esta classe:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws IOException {

                //Fluxo de Entrada com Arquivo
                InputStream fis = new FileInputStream("lorem.txt");
                InputStreamReader isr = new InputStreamReader(fis);
                BufferedReader br = BufferedReader(isr);

                String linha = br.readLine();

                while(linha != null) {
                        System.out.println(linha);
                        linha = br.readLine();
                }

                System.out.println(linha);

                br.close();
        }
}

O código continua funcionando. O próprio construtor do InputStreamReader funciona com um InputStream, não há necessidade de utilizarmos o tipo mais específico. Inclusive, ele é um Reader, e pode também ser representado pela classe mais genérica:

//Código omitido

public class TesteLeitura {

        public static void main(String[] args) throws IOException {

                //Fluxo de Entrada com Arquivo
                InputStream fis = new FileInputStream("lorem.txt");
                Reader isr = new InputStreamReader(fis);
                BufferedReader br = BufferedReader(isr);

                String linha = br.readLine();

                while(linha != null) {
                        System.out.println(linha);
                        linha = br.readLine();
                }

                System.out.println(linha);

                br.close();
        }
}

Na classe, vemos que ela estende a classe Reader:

//Código omitido

public class InputStreamReader extends Reader {

//Código omitido

Que por sua vez, é uma classe abstrata:

//Código omitido

public abstract class Reader implements Readable, Closeable {

//Código omitido

O mesmo é válido para a classe InputStream:

//Código omitido

public abstract class InputStream implements Closeable {

//Código omitido

Retornando à classe TesteLeitura, vemos que o BufferedReader é capaz de receber um Reader, ou seja, não há necessidade de ser um tipo específico.

As classes InputStream e Reader são chamadas templates, que são aquelas que pré-definem determinado conteúdo para as filhas.

Salvaremos e executaremos, o resultado no console permanece inalterado, indicando que nosso código continua funcionando.

Se observarmos a classe BufferedReader em detalhe:

//Código omitido

public class BufferedReader extends Reader {

//Código omitido

Veremos que ela também é um Reader, assim, poderíamos pensar que assim como fizemos anteriormente, também será possível a substituição pelo tipo menos específico. Entretanto, a classe Reader não possui o método readLine(), necessário para a leitura do nosso arquivo, sendo assim, precisamos manter o BufferedReader neste casso.

Adiante, veremos como fazer a saída. Até a próxima!

Sobre o curso Java e java.io: Streams, Reader e Writers

O curso Java e java.io: Streams, Reader e Writers possui 208 minutos de vídeos, em um total de 52 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Tá acabando

0

dias

00

hrs

00

min

00

seg

  • 1244 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

  • Desconto 20%

Premium

Desconto 20%
  • 1244 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75 R$60
à vista R$900 R$720
Matricule-se

Premium Plus

Desconto 20%
  • 1244 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100 R$80
à vista R$1.200 R$960
Matricule-se

Max

Desconto 20%
  • 1244 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120 R$96
à vista R$1.440 R$1.152
Matricule-se
Conheça os Planos para Empresas

Acesso por 1 ano

Estude 24h/dia onde e quando quiser

Novos cursos todas as semanas