CodeGym /Cursos /JAVA 25 SELF /Trabalhando com try-with-resources: fechamento automático...

Trabalhando com try-with-resources: fechamento automático de recursos

JAVA 25 SELF
Nível 36 , Lição 4
Disponível

1. Introdução

Trabalhar com arquivos em Java (e não só!) é sempre trabalhar com recursos externos. Quando você abre um arquivo, o sistema operacional aloca para o seu programa um “descritor” — um identificador especial que permite ler e escrever no arquivo. A quantidade desses descritores é limitada: se você não fechar os arquivos, o programa pode rapidamente “consumir” todos os recursos disponíveis e começar a lançar erros enigmáticos do tipo "Too many open files".

Além disso, se o arquivo não for fechado, ele pode permanecer bloqueado para outros programas. Por exemplo, você abriu um arquivo para escrita, esqueceu de fechá-lo e agora nem você nem outra pessoa conseguem alterá-lo ou excluí-lo. Uma espécie de “sequestro eterno” no mundo dos sistemas de arquivos.

Exemplo do mundo real

FileInputStream fis = new FileInputStream("data.txt");
int b = fis.read();
// ... fazemos algo e depois esquecemos de chamar fis.close()

Se o método close() não for chamado, o arquivo ficará “pendurado” até o término do programa. Em aplicações grandes, isso pode levar a vazamentos de recursos e até à queda da aplicação.

2. Método antigo: finally + close()

Até o Java 7, a maneira clássica de garantir o fechamento de um arquivo era assim:

FileInputStream fis = null;
try {
    fis = new FileInputStream("data.txt");
    // lendo o arquivo
    int b = fis.read();
    // ...
} catch (IOException e) {
    System.out.println("Erro ao ler o arquivo: " + e.getMessage());
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            System.out.println("Erro ao fechar o arquivo: " + e.getMessage());
        }
    }
}

Desvantagens dessa abordagem

  • É fácil esquecer o bloco finally e acabar com vazamento de recursos.
  • Muito código desnecessário, especialmente se houver vários streams.
  • Se ocorrer uma exceção ao fechar, também é preciso tratá-la separadamente.
  • O código fica pesado e menos legível.

3. Abordagem moderna: try-with-resources

Felizmente, no Java 7 surgiu uma sintaxe que resolve esses problemas de forma elegante e automática — try-with-resources.

Como é

try (FileInputStream fis = new FileInputStream("data.txt")) {
    int b = fis.read();
    // trabalhando com o arquivo
} catch (IOException e) {
    System.out.println("Erro ao trabalhar com o arquivo: " + e.getMessage());
}
// Aqui fis já está fechado automaticamente!

O principal: todos os recursos declarados entre parênteses após try serão fechados automaticamente após o término do bloco — mesmo que uma exceção ocorra no meio. Não é preciso escrever finally, nem capturar erros de fechamento separados — o Java faz tudo por você.

Quais classes podem ser usadas em try-with-resources?

Qualquer classe que implemente a interface AutoCloseable (ou a veterana Closeable). Isso abrange praticamente todos os streams padrão de I/O: FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, Scanner, PrintWriter, entre muitos outros.

4. Sintaxe do try-with-resources: detalhes e exemplos

Um recurso

try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Erro: " + e.getMessage());
}
// reader é fechado automaticamente!

Vários recursos

Você pode declarar vários recursos de uma vez separados por ponto e vírgula:

try (
    BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
    BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))
) {
    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.newLine();
    }
} catch (IOException e) {
    System.out.println("Erro ao copiar: " + e.getMessage());
}
// ambos os streams foram fechados!

Ordem de fechamento: os recursos são fechados na ordem inversa à sua declaração. Primeiro será chamado writer.close(), depois reader.close(). Isso é importante se um stream depender do outro.

Uso com classes personalizadas

Se você estiver escrevendo sua própria classe que trabalha com recursos, basta implementar a interface AutoCloseable:

class MyResource implements AutoCloseable {
    public void doSomething() {
        System.out.println("Trabalhando com o recurso!");
    }

    @Override
    public void close() {
        System.out.println("Recurso fechado!");
    }
}

try (MyResource res = new MyResource()) {
    res.doSomething();
}
// Após sair do bloco, será impresso: "Recurso fechado!"

5. Como funciona: diagrama

flowchart TD
    A[Abertura do recurso no try-with-resources] --> B{Ocorreu uma exceção no bloco try?}
    B -- Não --> C[O recurso é fechado automaticamente]
    B -- Sim --> D[O recurso é fechado automaticamente]
    D --> E[A exceção é propagada adiante]
    C --> F[O programa continua a execução]

Conclusão: independentemente de ter havido erro, o recurso sempre será fechado!

6. Exemplos: reescrevendo o código “do jeito novo”

Antes (estilo antigo):

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("input.txt"));
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Erro: " + e.getMessage());
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            System.out.println("Erro ao fechar: " + e.getMessage());
        }
    }
}

Depois (try-with-resources):

try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Erro: " + e.getMessage());
}
// Pronto, sem nenhum finally!

7. O que acontece com um erro durante o fechamento?

Às vezes, a própria operação de fechamento do recurso pode lançar uma exceção (por exemplo, se o arquivo desaparecer de repente). Em try-with-resources, tais exceções não se perdem: se já houve uma exceção no bloco try e, ao fechar o recurso, surgir uma segunda, ela será adicionada como “suprimida” (suppressed exception) à exceção principal. Isso pode ser visto com o método Throwable.getSuppressed().

Exemplo

try (MyResource res = new MyResource()) {
    throw new IOException("Erro no bloco try");
} catch (IOException e) {
    System.out.println("Erro principal: " + e.getMessage());
    for (Throwable suppressed : e.getSuppressed()) {
        System.out.println("Exceção suprimida: " + suppressed.getMessage());
    }
}

8. Quais classes suportam try-with-resources?

É simples: qualquer classe que implemente a interface AutoCloseable. Aqui estão algumas das padrão:

Classe Finalidade
FileInputStream
Leitura de bytes de um arquivo
FileOutputStream
Gravação de bytes em arquivo
FileReader/FileWriter
Leitura/gravação de texto
BufferedReader/Writer
Bufferização de streams
PrintWriter
Gravação de texto com formatação
Scanner
Leitura de dados de arquivo/console
ObjectInputStream/Output
Serialização/desserialização
ZipInputStream/Output
Trabalho com arquivos ZIP
Socket, ServerSocket
Conexões de rede

Se você usa bibliotecas de terceiros — consulte a documentação: se houver um método close(), é bem provável que a classe suporte try-with-resources.

9. Dicas e nuances úteis

Você pode declarar variáveis fora do try (a partir do Java 9): é possível usar recursos já declarados, se eles forem final ou “effectively final”:

BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
try (reader) {
    // ...
}

Funciona não só com arquivos: try-with-resources é útil para quaisquer recursos: conexões de rede, bancos de dados, quaisquer objetos com método close().

Não ignore as exceções: mesmo com try-with-resources, lembre-se de capturar e tratar exceções — não é uma panaceia, apenas uma forma prática de evitar vazamentos.

Não feche o recurso manualmente dentro do try: isso não é necessário — o Java fará isso por você! Se você chamar close() manualmente e depois o bloco try terminar, haverá uma tentativa de fechar um recurso já fechado. Geralmente é seguro, mas pode confundir.

10. Erros comuns ao usar try-with-resources

Erro nº 1: esquecer de usar try-with-resources. Se você ainda escreve finally { resource.close(); } — ou você está em 2011, ou não leu esta aula! Use a sintaxe moderna.

Erro nº 2: declarar o recurso fora do try e apenas usá-lo dentro. Esse código não fechará o recurso automaticamente:

BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
try {
    // ... usamos reader
} finally {
    // Mas aqui esquecemos de fechar!
}

Erro nº 3: chamar close() manualmente dentro do bloco try. Não é crítico, mas é redundante e pode levar a fechamento duplo. Simplesmente confie no Java.

Erro nº 4: capturar apenas Exception, ignorando a especificidade de E/S. É melhor capturar exceções específicas (FileNotFoundException, IOException) para fornecer mensagens claras ao usuário.

Erro nº 5: não tratar as exceções suprimidas. Se ocorrer um erro ao fechar o recurso, ele pode ser “suprimido”. Se você analisa erros, não se esqueça de getSuppressed().

1
Pesquisa/teste
Leitura e escrita de arquivos, nível 36, lição 4
Indisponível
Leitura e escrita de arquivos
Leitura e escrita de arquivos
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION