Entrada e Saída em Java

  1. Classificação de Streams

O pacote java.io fornece classes para manipulação de entrada e saída.

1.1 Streams de Entrada e Saída

A classe File não pode acessar o conteúdo do arquivo diretamente. Para isso, utiliza-se streams de entrada/saída. A direção é sempre relativa à memória: dados que entram na memória vêm de uma stream de entrada, e dados que saem da memória vão para uma stream de saída.

1.2 Streams de Bytes e Caracteres

Existem duas categorias principais:

  • Streams de bytes: InputStream, OutputStream
  • Streams de caracteres: Reader, Writer, BufferedReader

1.3 Streams de Nó e Streams de Processamento

Streams de processamento permitem adicionar funcionalidades aos streams de nó:

Exemplo de Streams de Processamento

FileOutputStream fos = new FileOutputStream("dados.txt");
PrintStream ps = new PrintStream(fos);
ps.println("Primeira linha de texto");
ps.println("Segunda linha de texto");
ps.close();
fos.close();
  1. Streams de Conversão

Apenas conversión entre streams de bytes e caracteres. Utiliza-se InputStreamReader e OutputStreamWriter.

InputStreamReader leitor = new InputStreamReader(System.in);
BufferedReader buffer = new BufferedReader(leitor);
String linha = buffer.readLine();
System.out.println("Digitado: " + linha);
buffer.close();
  1. Stream de Pushback

Permite retornar caracteres ao buffer de entrada.

  1. Redirecionamento de Entrada/Saída Padrão

System.setErr(PrintStream erro);
System.setIn(InputStream entrada);
System.setOut(PrintStream saida);
  1. Leitura e Escrita de Dados de Outros Processos

InputStream fluxoErro = processo.getErrorStream();
InputStream fluxoEntrada = processo.getInputStream();
OutputStream fluxoSaida = processo.getOutputStream();

Exemplo de Execução de Processo

Process processo = Runtime.getRuntime().exec("java -version");
BufferedReader leitor = new BufferedReader(
    new InputStreamReader(processo.getErrorStream()));
String conteudo = null;
while ((conteudo = leitor.readLine()) != null) {
    System.out.println(conteudo);
}
leitor.close();

Utilização do Scanner

Scanner sc = new Scanner(System.in);
String entrada = sc.nextLine();
  1. RandomAccessFile

Esta classe permite leitura e escrita direta em arquivos. É possível mover o ponteiro para qualquer posição. Atenção: ao inserir dados no meio do arquivo, o conteúdo a partir da posição do ponteiro será sobrescrito.

Métodos para操作指针:

  • long getFilePointer(): retorna a posição atual do ponteiro
  • void seek(long pos): move o ponteiro para uma posição específica

Métodos de Leitura/Escrita:

  • read()
  • write()

Exemplo de Uso

RandomAccessFile arquivo = new RandomAccessFile("documento.txt", "r");
arquivo.seek(100);
int byteLido = arquivo.read();
System.out.println("Byte na posição 100: " + byteLido);
arquivo.close();
  1. Serialização de Objetos

Uma classe deve implementar Serializable ou Externalizable para ser serializável. Geralmente, cada JavaBean implementa Serializable.

7.1 Utilizando Streams de Objetos

ObjectOutputStream oos = new ObjectOutputStream(
    new FileOutputStream("objeto.dat"));
Pessoa pessoa = new Pessoa("Maria", 25);
oos.writeObject(pessoa);
oos.close();
ObjectInputStream ois = new ObjectInputStream(
    new FileInputStream("objeto.dat"));
Pessoa p = (Pessoa) ois.readObject();
System.out.println("Nome: " + p.getNome());
ois.close();

Observação importante: A desserialização lê dados do objeto, não da classe. Portanto, o arquivo .class deve estar disponível. O mecanismo de desserialização não utiliza construtores.

Quando um objeto é serializado múltiplas vezes, apenas a primeira serialização gera a sequência de bytes completa. As subsequentes apenas gravam uma referência ao identificador já existente. Além disso, alterações feitas no objeto após a primeira serialização não são incluídas em serializações posteriores.

7.2 Serialização Personalizada

Para excluir um campo da serialização:

private transient int idade;

Para personalização avançada: antes da serialização, o método writeReplace() é chamado, seguido de writeObject(). Na desserialização, readObject() é executado primeiro, e depois readResolve(), cujo retorno substitui o resultado anterior. Isso é útil para classes singleton e enumerações.

Itens serializados: nome da classe, variáveis de instância (incluindo tipos primitivos, arrays e referências a outros objetos). Não são serializados: métodos, variáveis de classe e variáveis de instância transient.

7.3 Implementando Externalizable

A interface Externalizable permite controlar complteamente o processo de serialização.

  1. NIO (New Input/Output)

8.1 Buffer

Representa um container de dados com capacidade limitada. Tipos disponíveis:

  • CharBuffer
  • ByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

CharBuffer e ByteBuffer são os mais utilizados.

Relação entre índices: 0 ≤ mark ≤ position ≤ limit ≤ capacity

Métodos Principais

  • allocate(int capacidade): cria um buffer
  • flip(): alterna para modo de leitura
  • clear(): limpa o buffer para reuse

Exemplo de Uso

CharBuffer buffer = CharBuffer.allocate(10);
System.out.println("Capacidade: " + buffer.capacity());
System.out.println("Limite: " + buffer.limit());
System.out.println("Posição: " + buffer.position());

System.out.println("\nInserindo dados:");
buffer.put('X');
System.out.println("Posição = " + buffer.position());
buffer.put('Y');
System.out.println("Posição = " + buffer.position());
buffer.put('Z');
System.out.println("Posição = " + buffer.position());

System.out.println("\nAlterando para modo de leitura:");
buffer.flip();
System.out.println("Limite = " + buffer.limit());
System.out.println("Posição = " + buffer.position());

System.out.println("\nLendo dados:");
System.out.println(buffer.get());
System.out.println("Posição = " + buffer.position());
System.out.println(buffer.get());
System.out.println("Posição = " + buffer.position());
System.out.println(buffer.get());
System.out.println("Posição = " + buffer.position());

System.out.println("\nLimpando buffer:");
buffer.clear();
System.out.println("Limite = " + buffer.limit());
System.out.println("Posição = " + buffer.position());

8.2 Channel

Um canal representa uma conexão aberta com um dispositivo de E/S, como um arquivo ou soquete. O programa não pode acessar diretamente os dados do canal; deve interagir através de um buffer.

Tipos de Channel

  • FileChannel
  • DatagramChannel
  • Pipe.SinkChannel
  • Pipe.SourceChannel

Todos os canais são obtidos através do método getChannel() das classes de streams de nó.

Métodos do Channel

  • map(): mapeia uma região do arquivo em memória
  • read(): lê dados para o buffer
  • write(): escreve dados do buffer

8.3 Charset

Utilizado para converter entre sequências de bytes e caracteres.

import java.nio.charset.*;
import java.nio.*;

public class TesteCharset {
    public static void main(String[] args) throws CharacterCodingException {
        Charset utf8 = Charset.forName("UTF-8");
        CharsetEncoder codificador = utf8.newEncoder();
        CharsetDecoder decodificador = utf8.newDecoder();
        
        CharBuffer texto = CharBuffer.allocate(10);
        texto.put('B');
        texto.put('r');
        texto.put('a');
        texto.put('s');
        texto.put('i');
        texto.put('l');
        texto.flip();
        
        ByteBuffer bytes = codificador.encode(texto);
        for (int i = 0; i < bytes.capacity(); i++) {
            System.out.println(bytes.get(i));
        }
        
        System.out.println("\nTexto decodificado: " + decodificador.decode(bytes));
    }
}

8.4 Bloqueio de Arquivos

O NIO permite bloquear seções de um arquivo para acesso exclusivo:

  • lock(): bloqueia permanentemente até release
  • tryLock(): tenta bloquear, retorna null se não conseguir

Publicado em 7-20 05:30