Erros Checked vs Unchecked
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 61 de 87.
Você viu que a hierarquia de exceções do Java se divide em diferentes ramos. A distinção fundamental que você precisa entender é entre exceções checked e unchecked—isso afeta como você deve tratá-las em seu código.
Exceções verificadas (Checked exceptions) são exceções que o compilador obriga você a tratar. Elas estendem Exception diretamente (não através de RuntimeException). Você deve capturá-las ou declará-las com throws:
// Deve lidar com IOException - ela é verificada
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // Pode lançar IOException
}
// Ou capture-a
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}Exceções não verificadas estendem RuntimeException. O compilador não exige que você as trate — elas geralmente indicam erros de programação, como referências nulas ou acesso inválido a arrays:
// Nenhuma declaração throws necessária
public int divide(int a, int b) {
return a / b; // Pode lançar ArithmeticException (não verificada)
}
String name = null;
name.length(); // NullPointerException (não verificada)Ao criar exceções personalizadas, escolha a classe pai com base em como você deseja que os chamadores as manipulem. Use exceções verificadas para condições recuperáveis que o chamador deve antecipar. Use exceções não verificadas para erros de programação que não devem ocorrer em um código correto.
Desafio
FácilVamos construir um sistema de verificação de idade que demonstra a principal diferença entre exceções verificadas (checked) e não verificadas (unchecked)! Você criará exceções personalizadas de ambos os tipos e verá como o compilador as trata de forma diferente.
Você organizará seu código em quatro arquivos:
InvalidAgeException.java: Crie uma exceção verificada (checked exception) para falhas de validação de idade. Esta exceção deve estenderExceptiondiretamente, o que significa que os chamadores serão forçados a tratá-la. Inclua um construtor que aceite uma mensagem e a passe para a classe pai.NegativeAgeException.java: Crie uma exceção não verificada (unchecked exception) para quando alguém fornecer uma idade negativa (um erro de programação que não deveria acontecer com uma entrada correta). Esta deve estenderRuntimeException. Inclua um construtor que aceite e passe uma mensagem para o pai.AgeVerifier.java: Crie uma classe que valide idades usando ambos os tipos de exceção. Inclua dois métodos estáticos:verifyAge(int age)- Este método deve lançarNegativeAgeException(não verificada) com a mensagem"Age cannot be negative: " + agese a idade for menor que 0. Se a idade for menor que 18, lanceInvalidAgeException(verificada) com a mensagem"Must be 18 or older: " + age. Caso contrário, imprima"Age " + age + " verified successfully". Como este método lança uma exceção verificada, você deve declará-lo comthrows InvalidAgeException.verifyAgeUncheckedOnly(int age)- Este método usa apenas exceções não verificadas. Se a idade for negativa, lanceNegativeAgeExceptioncom o mesmo formato de mensagem. Se a idade for menor que 18, lance umaIllegalArgumentExceptioncom a mensagem"Too young: " + age. Caso contrário, imprima"Age " + age + " verified (unchecked method)". Note que este método não precisa de declaraçãothrows!Main.java: Reúna seu sistema de verificação! Você receberá uma entrada: uma idade (inteiro).Primeiro, imprima
"=== Checked Exception Method ==="e chameverifyAgecom a idade de entrada. Como ele lança uma exceção verificada, você deve envolvê-lo em um try-catch. CaptureInvalidAgeExceptione imprima"Checked exception caught: " + e.getMessage(). Também captureNegativeAgeExceptione imprima"Unchecked exception caught: " + e.getMessage().Em seguida, imprima uma linha em branco e
"=== Unchecked Exception Method ===". ChameverifyAgeUncheckedOnlycom a mesma idade. Embora este método possa lançar exceções, você não é forçado a capturá-las. No entanto, envolva-o em um try-catch de qualquer maneira para lidar com os erros graciosamente. CaptureNegativeAgeExceptione imprima"Runtime exception caught: " + e.getMessage(). CaptureIllegalArgumentExceptione imprima"Illegal argument caught: " + e.getMessage().
Você receberá uma entrada: um número inteiro representando a idade a ser verificada.
Observe a diferença fundamental: o método com a exceção verificada exige uma declaração throws e força os chamadores a tratá-la, enquanto o método não verificado compila normalmente sem qualquer tratamento de exceção. Esta é a distinção fundamental entre exceções checked e unchecked em Java!
Folha de consulta
As exceções em Java são divididas em exceções verificadas (checked) e não verificadas (unchecked), o que determina como o compilador impõe o tratamento delas.
Exceções verificadas estendem Exception diretamente (não através de RuntimeException). O compilador obriga você a capturá-las ou declará-las com throws:
// Deve declarar com throws
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path);
}
// Ou capturá-la
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}Exceções não verificadas estendem RuntimeException. O compilador não exige que você as trate. Elas geralmente indicam erros de programação:
// Nenhuma declaração throws é necessária
public int divide(int a, int b) {
return a / b; // Pode lançar ArithmeticException (não verificada)
}
String name = null;
name.length(); // NullPointerException (não verificada)Ao criar exceções personalizadas, escolha a classe pai com base em como os chamadores devem tratá-las:
- Use exceções verificadas (estenda
Exception) para condições recuperáveis que o chamador deve antecipar - Use exceções não verificadas (estenda
RuntimeException) para erros de programação que não devem ocorrer em um código correto
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int age = scanner.nextInt();
// TODO: Testar o método de exceção verificada (checked exception)
// Imprimir: "=== Checked Exception Method ==="
// Chamar AgeVerifier.verifyAge(age) em um try-catch
// - Catch InvalidAgeException: imprimir "Checked exception caught: [message]"
// - Catch NegativeAgeException: imprimir "Unchecked exception caught: [message]"
// TODO: Imprimir uma linha em branco, depois "=== Unchecked Exception Method ==="
// Chamar AgeVerifier.verifyAgeUncheckedOnly(age) em um try-catch
// - Catch NegativeAgeException: imprimir "Runtime exception caught: [message]"
// - Catch IllegalArgumentException: imprimir "Illegal argument caught: [message]"
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada10Tratamento de Exceções em POO
Hierarquia de Classes de ExceçãoExceções PersonalizadasErros Checked vs UncheckedPadrão Try With ResourcesRecapitulação - Usuário Validado2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator