Streams e NIO

Esta lição aborda IO baseado em fluxos do Java e NIO para operações com arquivos de baixo nível.

Arquitetura de IO Streams

TEXT
Fluxos de Entrada (Leitura)
InputStream (entrada de bytes)
├── FileInputStream (arquivo)
├── BufferedInputStream (buffer)
├── ByteArrayInputStream (array de bytes)
└── DataInputStream (tipos primitivos)

Reader (entrada de caracteres)
├── FileReader (arquivo)
├── BufferedReader (buffer)
└── InputStreamReader (conversão)

Fluxos de Saída (Escrita)
OutputStream (saída de bytes)
├── FileOutputStream (arquivo)
├── BufferedOutputStream (buffer)
└── DataOutputStream (tipos primitivos)

Writer (saída de caracteres)
├── FileWriter (arquivo)
├── BufferedWriter (buffer)
└── OutputStreamWriter (conversão)

Fluxos de Bytes

FileInputStream Leitura

JAVA
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("test.txt")) {
            int data;
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileOutputStream Escrita

JAVA
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            String text = "Olá, Mundo!";
            fos.write(text.getBytes());
            System.out.println("Escrita bem-sucedida");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Exemplo: Cópia de Arquivo com Fluxo de Bytes

JAVA
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteCopy {
    public static void copy(String src, String dest) throws IOException {
        try (FileInputStream fis = new FileInputStream(src);
             FileOutputStream fos = new FileOutputStream(dest)) {
            
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, length);
            }
        }
    }
    
    public static void main(String[] args) {
        try {
            copy("source.txt", "dest.txt");
            System.out.println("Cópia concluída");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
▶ Experimente

Fluxos com Buffer

Fluxos com buffer melhoram a eficiência do IO reduzindo a frequência de acessos ao disco.

BufferedInputStream/BufferedOutputStream

JAVA
import java.io.*;

public class BufferedStreamDemo {
    public static void main(String[] args) throws IOException {
        // Escrever
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("buffered.txt"))) {
            for (int i = 0; i < 1000; i++) {
                bos.write(("Linha " + i + "\n").getBytes());
            }
        }
        
        // Ler
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("buffered.txt"))) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = bis.read(buffer)) != -1) {
                System.out.write(buffer, 0, length);
            }
        }
    }
}

Fluxos de Dados

DataInputStream/DataOutputStream podem ler e escrever tipos primitivos.

Exemplo: Fluxos de Dados

JAVA
import java.io.*;

public class DataStreamDemo {
    public static void main(String[] args) throws IOException {
        // Escrever
        try (DataOutputStream dos = new DataOutputStream(
                new FileOutputStream("data.dat"))) {
            dos.writeInt(100);
            dos.writeDouble(3.14);
            dos.writeUTF("Olá");
        }
        
        // Ler
        try (DataInputStream dis = new DataInputStream(
                new FileInputStream("data.dat"))) {
            int i = dis.readInt();
            double d = dis.readDouble();
            String s = dis.readUTF();
            
            System.out.println("int: " + i);      // 100
            System.out.println("double: " + d);    // 3.14
            System.out.println("string: " + s);    // Olá
        }
    }
}
▶ Experimente

NIO (New IO)

Java NIO fornece operações de IO mais eficientes.

Interface Path

JAVA
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo {
    public static void main(String[] args) {
        Path path = Paths.get("/home/user/test.txt");
        
        System.out.println("Nome do arquivo: " + path.getFileName());  // test.txt
        System.out.println("Pai: " + path.getParent());    // /home/user
        System.out.println("Raiz: " + path.getRoot());      // /
        System.out.println("Contagem de nomes: " + path.getNameCount());  // 3
    }
}

Classe Utilitária Files

JAVA
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class FilesDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");
        
        // Verificar
        System.out.println("Existe: " + Files.exists(path));
        System.out.println("É arquivo: " + Files.isRegularFile(path));
        System.out.println("É legível: " + Files.isReadable(path));
        
        // Ler todas as linhas
        List<String> lines = Files.readAllLines(path);
        lines.forEach(System.out::println);
        
        // Ler como array de bytes
        byte[] bytes = Files.readAllBytes(path);
        System.out.println("Tamanho: " + bytes.length);
        
        // Escrever
        Files.write(Paths.get("output.txt"), "Olá".getBytes());
        
        // Criar diretório
        Files.createDirectories(Paths.get("newdir/subdir"));
        
        // Copiar
        Files.copy(Paths.get("source.txt"), Paths.get("dest.txt"));
        
        // Mover
        Files.move(Paths.get("old.txt"), Paths.get("new.txt"));
        
        // Deletar
        Files.deleteIfExists(Paths.get("temp.txt"));
    }
}

Atributos de Arquivo

JAVA
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;

public class FileAttributes {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");
        
        BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
        
        System.out.println("Tamanho: " + attrs.size());
        System.out.println("Criado: " + attrs.creationTime());
        System.out.println("Modificado: " + attrs.lastModifiedTime());
        System.out.println("É diretório: " + attrs.isDirectory());
        System.out.println("É arquivo: " + attrs.isRegularFile());
    }
}

Percorrer Arquivos (NIO)

JAVA
import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Stream;

public class NIODirectoryWalk {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get(".");
        
        // Listar diretório
        try (Stream<Path> stream = Files.list(dir)) {
            stream.forEach(path -> {
                String type = Files.isDirectory(path) ? "[DIR]" : "[ARQ]";
                System.out.println(type + " " + path.getFileName());
            });
        }
        
        // Percorrer recursivamente
        try (Stream<Path> stream = Files.walk(dir, 3)) {  // Máximo 3 níveis
            stream.forEach(path -> System.out.println(path));
        }
        
        // Encontrar arquivos
        try (Stream<Path> stream = Files.find(dir, 10, 
                (path, attrs) -> attrs.isRegularFile() && path.toString().endsWith(".txt"))) {
            stream.forEach(System.out::println);
        }
    }
}

IO vs NIO

Recurso IO NIO
Modelo Orientado a fluxo Orientado a buffer
Bloqueio IO bloqueante IO não bloqueante
Seletores Nenhum Sim
API Mais antiga Mais nova

Guia de Seleção

Cenário Recomendação
Leitura/escrita simples de arquivos Classe utilitária Files
Processamento de arquivos grandes Fluxos com buffer
Rede de alta concorrência NIO
Manutenção de código legado Fluxos IO

❓ Perguntas Frequentes

P: Como escolher entre fluxos de bytes e fluxos de caracteres? R: Use fluxos de caracteres para arquivos de texto, fluxos de bytes para arquivos binários.

P: Quais são os benefícios dos fluxos com buffer? R: Reduzem a frequência de acessos ao disco, melhorando a eficiência do IO.

P: NIO é mais rápido que IO? R: Não necessariamente. As principais vantagens do NIO são não bloqueio e seletores. Para operações simples com arquivos, a diferença é mínima.

📖 Resumo

📝 Exercícios

  1. Comparação de arquivos: Compare dois arquivos para verificar se são idênticos
  2. Processamento de arquivos grandes: Leia arquivos grandes em pedaços, conte linhas
  3. Sincronização de diretórios: Sincronize um diretório para outro

Próxima Lição

Na próxima lição, aprenderemos sobre Regex e JSON — técnicas avançadas de processamento de strings.

100%