Menu
Coddy logo textTech

Hierarquia de Classes de Exceção

Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 59 de 87.

O sistema de tratamento de exceções do Java é construído sobre uma hierarquia de classes bem organizada enraizada na classe Throwable. Entender essa hierarquia ajuda você a capturar e tratar exceções de forma mais eficaz.

No topo está o Throwable, o pai de todas as exceções e erros. Ele possui duas subclasses diretas:

Throwable
├── Error          // Problemas sérios (não capture estes)
└── Exception      // Problemas recuperáveis
    └── RuntimeException  // Exceções não verificadas

Error representa problemas sérios que as aplicações não devem tentar tratar — como OutOfMemoryError ou StackOverflowError. Estes indicam problemas fundamentais com a própria JVM.

Exception é o ramo com o qual você mais trabalhará. Ele se divide em duas categorias: exceções verificadas (subclasses diretas de Exception) e exceções não verificadas (subclasses de RuntimeException).

// Classes de exceção comuns
Exception
├── IOException           // Problemas de arquivo/rede
├── SQLException          // Problemas de banco de dados
└── RuntimeException
    ├── NullPointerException
    ├── ArrayIndexOutOfBoundsException
    └── IllegalArgumentException

Como as exceções são classes, elas seguem as regras de herança. Quando você captura um tipo de exceção pai, você também captura todos os seus filhos:

try {
    // algum código
} catch (Exception e) {
    // Captura TODAS as exceções (geralmente amplo demais)
}
try {
    // algum código
} catch (RuntimeException e) {
    // Captura apenas exceções de tempo de execução e subclasses
}

Esta hierarquia permite que você seja tão específico ou geral quanto necessário ao tratar erros, e é a base para criar suas próprias exceções personalizadas.

challenge icon

Desafio

Fácil

Vamos construir um analisador de exceções que demonstra como a hierarquia de exceções do Java funciona na prática! Você criará um sistema que captura exceções em diferentes níveis de especificidade e relata qual tipo foi capturado.

Você organizará seu código em três arquivos:

  • ExceptionThrower.java: Crie uma classe que possa disparar diferentes tipos de exceções com base na entrada. Sua classe deve ter três métodos estáticos:

    triggerRuntime(String type) - Com base na string de tipo, lance diferentes exceções de tempo de execução (runtime exceptions):

    • Se o tipo for "null", lance uma NullPointerException com a mensagem "Null value encountered"
    • Se o tipo for "index", lance uma ArrayIndexOutOfBoundsException com a mensagem "Invalid index"
    • Se o tipo for "argument", lance uma IllegalArgumentException com a mensagem "Bad argument"

    triggerArithmetic() - Lance uma ArithmeticException com a mensagem "Division error"

    triggerGeneric() - Lance uma RuntimeException com a mensagem "Generic runtime error"

  • ExceptionAnalyzer.java: Crie uma classe que capture exceções em diferentes níveis da hierarquia e identifique o que foi capturado. Inclua estes métodos estáticos:

    analyzeSpecific(String type) - Chame ExceptionThrower.triggerRuntime(type) dentro de um bloco try. Use blocos catch separados para NullPointerException, ArrayIndexOutOfBoundsException e IllegalArgumentException. Para cada um, imprima: Caught specific: [exception class simple name] seguido por Message: [exception message]

    analyzeWithParent() - Chame ExceptionThrower.triggerArithmetic() dentro de um bloco try. Capture-a usando RuntimeException (o tipo pai). Imprima: Caught via parent: RuntimeException seguido por Actual type: [exception class simple name]

    analyzeWithGrandparent() - Chame ExceptionThrower.triggerGeneric() dentro de um bloco try. Capture-a usando Exception (ainda mais alto na hierarquia). Imprima: Caught via grandparent: Exception seguido por Actual type: [exception class simple name]

  • Main.java: Reúna seu analisador de exceções! Você receberá uma entrada: uma string de tipo de exceção (seja "null", "index" ou "argument").

    Primeiro, imprima === Specific Catch === e chame analyzeSpecific com sua entrada.

    Depois, imprima uma linha em branco, === Parent Catch ===, e chame analyzeWithParent.

    Finalmente, imprima uma linha em branco, === Grandparent Catch ===, e chame analyzeWithGrandparent.

Você receberá uma entrada: o tipo de exceção a ser disparado ("null", "index" ou "argument").

Use e.getClass().getSimpleName() para obter o nome da classe da exceção e e.getMessage() para recuperar sua mensagem. Este desafio mostra como capturar um tipo de exceção pai também captura todos os seus filhos — um conceito fundamental ao decidir quão específico deve ser o seu tratamento de exceções!

Folha de consulta

O tratamento de exceções do Java é construído sobre uma hierarquia de classes enraizada em Throwable:

Throwable
├── Error          // Problemas sérios (não capture)
└── Exception      // Problemas recuperáveis
    └── RuntimeException  // Exceções não verificadas

Error representa problemas sérios da JVM, como OutOfMemoryError ou StackOverflowError, que as aplicações não devem tratar.

Exception divide-se em exceções verificadas (subclasses diretas) e exceções não verificadas (subclasses de RuntimeException):

Exception
├── IOException           // Problemas de arquivo/rede
├── SQLException          // Problemas de banco de dados
└── RuntimeException
    ├── NullPointerException
    ├── ArrayIndexOutOfBoundsException
    └── IllegalArgumentException

Capturar um tipo de exceção pai também captura todos os seus filhos:

try {
    // algum código
} catch (Exception e) {
    // Captura TODAS as exceções
}

try {
    // algum código
} catch (RuntimeException e) {
    // Captura apenas exceções de tempo de execução e subclasses
}

Use e.getClass().getSimpleName() para obter o nome da classe da exceção e e.getMessage() para recuperar sua mensagem.

Experimente você mesmo

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String type = scanner.nextLine();
        
        // TODO: Imprima "=== Specific Catch ===" e chame ExceptionAnalyzer.analyzeSpecific(type)
        
        // TODO: Imprima uma linha em branco, depois "=== Parent Catch ===", depois chame ExceptionAnalyzer.analyzeWithParent()
        
        // TODO: Imprima uma linha em branco, depois "=== Grandparent Catch ===", depois chame ExceptionAnalyzer.analyzeWithGrandparent()
    }
}
quiz iconTeste seus conhecimentos

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