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.
GO TO FULL VERSION