Diversas operações em Java podem ser realizadas através de mecanismos de fluxos de dados
denominados stream
. Streams representam fluxos de informação de entrada ou saída. Eles abstraem o envio e recuperação de dados para/de fontes externas (arquivos, dados pela rede etc.)
Os fluxos são representadas genericamente por duas classes abstratas:
Reader
- stream de entradaWriter
- stream de saídaHá uma hierarquia de herdeiros de Writers
de acordo com o propósito.
FileWriter
O FileWriter
é um fluxo de saída que guarda os dados em um arquivo. O caminho do arquivo é indicado no seu construtor como pode ser visto abaixo.
Como o FileWriter
é um fluxo em sua forma básica, ele não dispõe de operações mais alto nível para a gravação de dados. Portanto, caso se desejasse gravar a sequência "Tecodonte"
, seria necessário se gravar um byte de cada vez. Veja no código a seguir.
Em geral, fluxos precisam ser fechados com a operação close()
quando se conclui a operação com eles. Operações com arquivo, por exemplo, podem envolver cash e bloqueio de recursos, que são liberados quando o fluxo é fechado.
Exceções de entrada/saída (IOException
) geralmente são associadas a fluxos, pois podem haver erros, por exemplo, na leitura e gravação de dados.
In [2]:
import java.io.FileWriter;
import java.io.IOException;
FileWriter arquivo;
try {
arquivo = new FileWriter("texto1.txt");
arquivo.write('T');
arquivo.write('e');
arquivo.write('c');
arquivo.write('o');
arquivo.write('d');
arquivo.write('o');
arquivo.write('n');
arquivo.write('t');
arquivo.write('e');
arquivo.close();
System.out.println("Gravacao concluida com sucesso!");
} catch (IOException erro) {
System.out.println("Nao consegui criar o arquivo =(");
erro.printStackTrace();
}
Observe à esquerda que foi criado um arquivo chamado texto1.txt
com o conteúdo gravado. Pode ser necessário aguardar um pouco até o Jupyter sincronizar a visualização.
FileReader
Tal como o FileWriter
, o FileReader
representa um fluxo de entrada de um arquivo em sua forma básica. O código a seguir mostra como o FileReader
é usado para a leitura do que foi gravado antes, um byte de cada vez.
In [3]:
import java.io.FileReader;
import java.io.IOException;
try {
FileReader arquivo = new FileReader("texto1.txt");
int caractere = arquivo.read();
while (caractere != -1) {
System.out.println((char)caractere);
caractere = arquivo.read();
}
arquivo.close();
} catch (IOException erro) {
System.out.println("Nao consegui criar o arquivo =(");
erro.printStackTrace();
}
Design pattern usado pelo Java para concatenar fluxos.
Este pattern é bastante popular em sistemas operacionais UNIX-like. Trata-se de processo incremental em que, enquanto um elemento vai gerando um fluxo vai gerando os dados de saída, o elemento seguinte vai consumindo o fluxo sem esperar que a entrada de dados se complete.
Em Java os streams podem trabalhar sob a lógica de Pipe & Filter se conectando fluxos. Veja no slide a seguir uma ilustração de como o FileWriter
pode se conectar ao PrintWriter
para permitir operações de nível mais alto. O PrintWriter
oferece operações de mais alto nível, como o println()
. Essa operação recebe uma String e a decompõe em bytes que são entregues ao fluxo seguinte.
Como o PrintWriter
não tem a funcionalidade de gravar em arquivos, os fluxos são conectados para trabalhar em colaboração:
No código a seguir é apresentada a sequência para se conectar fluxos. No slide a seguir é comentada a sequência de passos que estão no código:
Note que o resultado final é o mesmo da sequência anterior (o println
acrescenta mais um enter
no final do fluxo).
In [1]:
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
FileWriter arquivo;
PrintWriter formatado;
try {
arquivo = new FileWriter("texto2.txt");
formatado = new PrintWriter(arquivo);
formatado.println("Tecodonte");
formatado.close();
System.out.println("Gravacao realizada com sucesso!");
} catch (IOException erro) {
System.out.println("Nao consegui criar o arquivo =(");
}
Reader
Da mesma forma que na gravação, é possível se realizar Pipe & Filter com o Reader
. No exemplo a seguir, é usado o BufferedReader
conectado ao FileReader
. Nesse caso, o BufferedReader
tem um papel equivalente ao PrintWriter
, oferecendo operações de alto nível de leitura como o readLine
que lê uma linha (String) completa.
In [2]:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
try {
FileReader arquivo = new FileReader("texto2.txt");
BufferedReader formatado = new BufferedReader(arquivo);
String linha = formatado.readLine();
while (linha != null)
{
System.out.println(linha);
linha = formatado.readLine();
}
arquivo.close();
} catch (IOException erro) {
erro.printStackTrace();
}
Da forma que foi mostrado até agora, cada vez que você cria um arquivo ele reinicializa seu conteúdo, ou seja, se havia algum conteúdo anteriormente no arquivo, ele é apagado.
É possível sinalizar ao Java que você deseja gravar novos dados no final do arquivo; isso é feito acrescentando-se um parâmero true
na criação do FileWriter
:
In [2]:
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
FileWriter arquivo;
PrintWriter formatado;
try {
// o segundo parametro indica se fara append ou nao
arquivo = new FileWriter("texto2.txt", true);
formatado = new PrintWriter(arquivo);
formatado.println("amigo do Horacio");
formatado.close();
System.out.println("Gravacao realizada com sucesso!");
} catch (IOException erro) {
System.out.println("Nao consegui criar o arquivo =(");
}
Retomando o exemplo do Empréstimo codificado a seguir (classe geral Emprestimo
que abstrai duas subclasses: EmprestimoSimples
e EmprestimoComposto
), escreva duas rotinas:
Nesse exercício, não pode ser usado o recurso nativo de serialização do Java. O método de serialização e deserialização deve ser implementado por você.
In [2]:
import java.lang.Math;
public abstract class Emprestimo {
protected float s;
protected int n;
protected float j;
protected int corrente;
protected float p,
proxima;
public Emprestimo(float s, int n, float j) {
this.s = s;
this.n = n;
this.j = j;
corrente = 1;
this.p = -1; // antes da primeira parcela
this.proxima = s;
}
float getS() {
return s;
}
int getN() {
return n;
}
float getJ() {
return j;
}
public float parcela() {
return p;
}
public abstract float proximaParcela();
public abstract float parcela(int numero);
}
class EmprestimoSimples extends Emprestimo {
public EmprestimoSimples(float s, int n, float j) {
super(s, n, j);
}
public float proximaParcela() {
if (corrente <= n)
p = s + ((corrente-1) * s * (j/100));
else
p = 0;
corrente++;
return p;
}
public float parcela(int numero) {
float resultado = 0;
if (numero <= n)
resultado = s + ((numero-1) * s * (j/100));
return resultado;
}
}
class EmprestimoComposto extends Emprestimo {
public EmprestimoComposto(float s, int n, float j) {
super(s, n, j);
}
public float proximaParcela() {
p = proxima;
corrente++;
if (corrente <= n)
proxima += (proxima * (j/100));
else
proxima = 0;
return p;
}
public float parcela(int numero) {
float resultado = 0;
if (numero <= n)
resultado = s * (float)Math.pow(1 + j/100, numero-1);
return resultado;
}
}
// codigo principal
Emprestimo emprestimo1 = new EmprestimoSimples(500, 7, 2);
Emprestimo emprestimo2 = new EmprestimoComposto(500, 7, 2);
int i = 1;
emprestimo1.proximaParcela();
emprestimo2.proximaParcela();
while (emprestimo1.parcela() > 0 || emprestimo2.parcela() > 0) {
if (emprestimo1.parcela() > 0) {
System.out.println("Emprestimo 1: parcela " + i + " eh " + emprestimo1.parcela());
System.out.println(" parcela " + i + " eh " + emprestimo1.parcela(i));
}
if (emprestimo2.parcela() > 0) {
System.out.println("Emprestimo 2: parcela " + i + " eh " + emprestimo2.parcela());
System.out.println(" parcela " + i + " eh " + emprestimo2.parcela(i));
}
emprestimo1.proximaParcela();
emprestimo2.proximaParcela();
i++;
}
In [ ]:
Para testar a sua implementação, replique também o exemplo acima com EmprestimoSimples
e EmprestimoComposto
a seguir no seu código de tal forma que na primeira parte são criados os objetos e serializados, na segunda parte eles são lidos, deserializados e as parcelas dos empréstimos são impressas na tela.
In [ ]:
In [ ]: