CodeGym /Cursos /JAVA 25 SELF /Exceções como parte da API e try-with-resources

Exceções como parte da API e try-with-resources

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

1. Exceções como parte da API

Por que as exceções são parte do “contrato” de um método?

Ao escrever um método, você define não apenas os parâmetros e o valor de retorno, mas também quais exceções ele pode lançar. Isso é parte do “contrato” entre o seu método e quem vai usá-lo. Se um método pode lançar uma exceção, todos que o chamam devem saber — para tratar o erro corretamente (ou ao menos não se surpreender quando o programa encerrar de forma abrupta com um erro).

Exemplo:

public void readFile(String filename) throws IOException {
    // ... leitura do arquivo
}

Aqui fica explícito que o método pode lançar IOException. Isso é um sinal para o usuário do método: “Esteja pronto para tratar um erro de leitura de arquivo!”

Documentando exceções: anotação @throws no Javadoc

Para que outros desenvolvedores entendam quais exceções seu método pode lançar, use a anotação @throws (ou @exception) no Javadoc.

Exemplo:

/**
 * Lê o conteúdo de um arquivo.
 *
 * @param filename nome do arquivo
 * @return conteúdo do arquivo como String
 * @throws IOException se ocorrer um erro de leitura do arquivo
 */
public String readFile(String filename) throws IOException {
    // ...
}

Por que isso é útil?

  • Ajuda outros desenvolvedores a entender quais erros precisam ser tratados.
  • As IDEs e geradores de documentação (por exemplo, o Javadoc) exibem essas exceções diretamente nas dicas.
  • Aumenta a confiabilidade e a previsibilidade do código.

Exceções checked e unchecked na API

Exceções checked (subclasses de Exception, mas não de RuntimeException) — fazem parte do contrato do método. Elas precisam ser tratadas ou explicitamente propagadas (throws).

Exceções unchecked (RuntimeException e suas subclasses) — normalmente sinalizam erros de programação (por exemplo, NullPointerException, IllegalArgumentException). Não é obrigatório declará-las na assinatura, mas, se seu método pode lançar esse tipo de erro (por exemplo, para argumentos inválidos), também vale descrevê-lo no Javadoc.

Exemplo:

/**
 * Divide a por b.
 * @param a dividendo
 * @param b divisor
 * @return resultado da divisão
 * @throws IllegalArgumentException se b == 0
 */
public int divide(int a, int b) {
    if (b == 0) throw new IllegalArgumentException("O divisor não pode ser zero");
    return a / b;
}

Exceções e o design da API

  • Pense no usuário do seu método: quais erros ele pode e deve tratar? Quais são bugs e quais são situações “esperadas”?
  • Não abuse de exceções checked: se o erro é um bug (por exemplo, argumento inválido), prefira lançar uma exceção unchecked.
  • Documente todas as exceções que podem ser propagadas “para cima”.

2. A construção try-with-resources

Problema: como fechar recursos com segurança?

Em muitas tarefas é preciso trabalhar com recursos que devem ser fechados obrigatoriamente após o uso: arquivos, conexões de rede, bancos de dados etc. Se você esquecer de fechar um recurso, pode haver vazamento de memória, bloqueio de arquivo ou outros problemas.

Antes:

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    // ...
} catch (IOException e) {
    // tratamento do erro
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            // tratamento de erro ao fechar
        }
    }
}

Muito código, fácil cometer erros, e é possível esquecer de fechar o recurso.

Solução: try-with-resources

Desde o Java 7, existe a construção try-with-resources, que fecha automaticamente todos os recursos, mesmo se ocorrer uma exceção dentro do bloco.

Sintaxe:

try (ResourceType resource = new ResourceType(...)) {
    // uso do recurso
} catch (ExceptionType e) {
    // tratamento do erro
}

Exemplo:

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Erro ao ler o arquivo: " + e.getMessage());
}
// reader.close() será chamado automaticamente!

Como isso funciona?

  • Entre parênteses após try são declarados os recursos que precisam ser fechados.
  • Ao sair do bloco try (mesmo se ocorrer uma exceção!), o método close() é chamado para cada recurso.
  • Isso funciona apenas para recursos que implementam a interface AutoCloseable (ou seu ancestral Closeable).

Interface AutoCloseable:

public interface AutoCloseable {
    void close() throws Exception;
}

Todos os recursos padrão do Java (arquivos, fluxos, conexões com BD) implementam essa interface.

É possível declarar vários recursos

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();
    }
}

Ambos os recursos serão fechados automaticamente, mesmo se ocorrer uma exceção.

Vantagens do try-with-resources

  • Segurança: os recursos sempre são fechados, mesmo em caso de erro.
  • Concisão: menos código, menos chance de errar.
  • Legibilidade: fica claro quais recursos são usados e quando são fechados.

3. Prática: escrevendo código seguro com try-with-resources

Exemplo: leitura de arquivo

public static void printFirstLine(String filename) {
    try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
        String line = reader.readLine();
        System.out.println("Primeira linha: " + line);
    } catch (IOException e) {
        System.out.println("Erro: " + e.getMessage());
    }
}

Exemplo: escrita em arquivo

public static void writeToFile(String filename, String text) {
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
        writer.write(text);
    } catch (IOException e) {
        System.out.println("Erro ao gravar: " + e.getMessage());
    }
}

Exemplo: recurso próprio

Se você estiver escrevendo sua própria classe que precisa ser fechada, basta implementar AutoCloseable:

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

Agora ele pode ser usado em try-with-resources:

try (MyResource res = new MyResource()) {
    // uso do recurso
} 

4. Erros comuns e boas práticas

Erro nº 1: esqueceu de fechar o recurso (sem try-with-resources).
Se não usar try-with-resources, é fácil esquecer de fechar um arquivo ou fluxo — isso leva a vazamentos de recursos.

Erro nº 2: tentar usar try-with-resources com um objeto que não implementa AutoCloseable.
Se sua classe não implementa essa interface, o compilador não permitirá usá-la em try-with-resources.

Erro nº 3: não documentar as exceções na API.
Se seu método pode lançar uma exceção — indique isso na assinatura (throws) e no Javadoc (@throws). Isso ajuda outras pessoas a usar seu código corretamente.

Erro nº 4: capturar Exception em vez de erros específicos.
Prefira capturar apenas as exceções que você realmente espera e sabe tratar.

1
Pesquisa/teste
Hierarquia de exceções, nível 24, lição 4
Indisponível
Hierarquia de exceções
Trabalho avançado com exceções
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION