Especialização em Sistemas com Java 21 LTS • Módulo de Herança e Contratos


🗺️ Mapa Conceitual do Tópico

flowchart TD
    A["java.lang.Throwable"] --> B["java.lang.Error (Crítico / Fatal)"]
    A --> C["java.lang.Exception (Recuperável)"]

    B --> D["OutOfMemoryError / StackOverflowError"]

    C --> E["Checked Exceptions (javac exige throws / try-catch)"]
    C --> F["java.lang.RuntimeException (Unchecked)"]

    E --> G["IOException / SQLException / ParseException"]
    F --> H["NullPointerException / IllegalArgumentException / RegraNegocioException"]

    subgraph MANIPULACAO["Mecanismos Robustos em Java 21"]
        I["try-with-resources (AutoCloseable)"]
        J["Multi-catch (catch IOException | SQLException e)"]
        K["Exception Chaining (initCause / getCause)"]
    end

    C --> MANIPULACAO

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#ffebee,stroke:#f44336,stroke-width:2px
    style E fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style F fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style I fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

💥 1. A Hierarquia Throwable da JVM

Na plataforma Java, qualquer entidade que possa ser lançada (throw) ou capturada (catch) deve herdar direta ou indiretamente de java.lang.Throwable. A árvore de tipos divide-se em duas ramificações fundamentais:

1. java.lang.Error

Representa condições de falha severa na infraestrutura do ambiente de execução (JVM ou hardware do servidor).

  • Exemplos: OutOfMemoryError, StackOverflowError, InternalError.
  • Diretriz: Aplicações comuns nunca devem tentar capturar Error, pois a integridade do runtime e a consistência da memória estão comprometidas.

2. java.lang.Exception

Representa condições excepcionais e anômalas das quais o programa pode razoavelmente se recuperar ou aplicar contingência.


⚖️ 2. Checked vs Unchecked Exceptions

O compilador javac impõe regras estritas de verificação de tipos nas exceções:

Checked Exceptions (extends Exception)

  • Verificação em Compilação: O compilador exige obrigatoriamente que o método declare o lançamento (throws MinhaExcecao) ou faça a captura interna através de um bloco try-catch.
  • Cenário Típico: Falhas externas imprevisíveis sobre as quais o código local não tem controle absoluto (falha de rede, arquivo não encontrado, indisponibilidade temporária de banco).

Unchecked Exceptions (extends RuntimeException)

  • Isenção de Verificação: Não exigem declaração explícita na assinatura do método.
  • Cenário Típico: Erros de programação e violação de contratos e invariantes da API (NullPointerException, IndexOutOfBoundsException, IllegalArgumentException, IllegalStateException).
  • Design Moderno: Em arquiteturas corporativas contemporâneas (Spring Boot, Quarkus), a quase totalidade das exceções de regra de negócio herda de RuntimeException para evitar poluição de assinaturas em camadas de serviço.

🛡️ 3. O Mecanismo Moderno try-with-resources e AutoCloseable

Antes do Java 7, o fechamento de recursos como arquivos, conexões JDBC e sockets exigia blocos finally manuais propensos a vazamento de ponteiros ou mascaramento de exceções originais.

O Padrão try-with-resources:

Qualquer classe que implemente a interface funcional java.lang.AutoCloseable pode ser gerenciada automaticamente pelo compilador:

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement(SQL)) {
    // Processamento do recurso
} catch (SQLException e) {
    logger.error("Falha na consulta SQL", e);
} // conn e ps são encerrados automaticamente aqui, mesmo em caso de erro!

Exceções Suprimidas (Suppressed Exceptions):

Se uma exceção for lançada no corpo do bloco try e o método .close() automático do recurso também lançar uma exceção secundária, a JVM preserva a exceção do bloco principal e anexa a segunda como suprimida, acessível via e.getSuppressed().


🎯 4. Encadeamento de Exceções (Exception Chaining) e Exceções de Domínio

Nunca capture uma exceção de baixo nível (como SQLException) para simplesmente imprimir no console e continuar a execução. Em arquiteturas limpas, converta a falha técnica em uma exceção de domínio corporativa sem perder a raiz do problema:

public class SaldoInsuficienteException extends RuntimeException {
    private final BigDecimal saldoDisponivel;
    private final BigDecimal valorTentado;

    public SaldoInsuficienteException(BigDecimal saldo, BigDecimal valor) {
        super(String.format("Saldo insuficiente: R$ %.2f para saque de R$ %.2f", saldo, valor));
        this.saldoDisponivel = saldo;
        this.valorTentado = valor;
    }

    // Construtor com encadeamento de causa técnica
    public SaldoInsuficienteException(String mensagem, Throwable causa) {
        super(mensagem, causa);
        this.saldoDisponivel = BigDecimal.ZERO;
        this.valorTentado = BigDecimal.ZERO;
    }
}

📊 5. Matriz Comparativa: Classificação de Falhas

Característica Error Checked Exception Unchecked RuntimeException
Classe Mãe java.lang.Throwable java.lang.Exception java.lang.RuntimeException
Checagem pelo javac Não Sim (Obrigatória) Não
Cláusula throws Opcional Obrigatória se não capturada Opcional (recomendada em Javadoc)
Recuperabilidade Não recuperável Recuperável via contingência Recuperável / Correção de código
Exemplo Real OutOfMemoryError FileNotFoundException IllegalArgumentException

💻 6. Código de Demonstração Corporativo (Java 21 LTS)

package com.empresa.excecoes;

import java.math.BigDecimal;

/**
 * Sistema financeiro com tratamento de exceções de domínio e encadeamento de causas.
 */
public class SistemaBancarioResiliente {

    public static class ContaInativaException extends RuntimeException {
        public ContaInativaException(String contaId) {
            super("A conta bancária [" + contaId + "] está cancelada ou bloqueada judicialmente.");
        }
    }

    public static class SaldoInsuficienteException extends RuntimeException {
        public SaldoInsuficienteException(BigDecimal saldoAtual, BigDecimal saqueDesejado) {
            super(String.format("Tentativa de débito de R$ %.2f excede o saldo disponível de R$ %.2f.", saqueDesejado, saldoAtual));
        }
    }

    public static class ServicoOperacaoFinanceira implements AutoCloseable {
        private final String canalAuditado;
        private boolean aberto = true;

        public ServicoOperacaoFinanceira(String canal) {
            this.canalAuditado = canal;
            System.out.printf("🔌 [CANAL] Conexão segura estabelecida via [%s]%n", canalAuditado);
        }

        public void debitar(String conta, BigDecimal saldo, BigDecimal valor, boolean ativa) {
            if (!aberto) throw new IllegalStateException("Canal de operação já encerrado.");
            if (!ativa) throw new ContaInativaException(conta);
            if (valor.compareTo(saldo) > 0) throw new SaldoInsuficienteException(saldo, valor);

            System.out.printf("   ✅ Débito de R$ %.2f concluído com sucesso na conta %s!%n", valor, conta);
        }

        @Override
        public void close() {
            this.aberto = false;
            System.out.printf("🔒 [CANAL] Conexão [%s] encerrada com sucesso (Flush de logs).%n", canalAuditado);
        }
    }

    public static void main(String[] args) {
        System.out.println("================================================================================");
        System.out.println("          PROCESSAMENTO TRANSACIONAL COM TRY-WITH-RESOURCES (JAVA 21)           ");
        System.out.println("================================================================================");

        // 1. Operação com Try-with-resources garantindo o fechamento da sessão
        try (ServicoOperacaoFinanceira servico = new ServicoOperacaoFinanceira("TERMINAL-AGENCIA-01")) {
            servico.debitar("CC-9901", new BigDecimal("1000.00"), new BigDecimal("350.00"), true);
            servico.debitar("CC-9902", new BigDecimal("200.00"), new BigDecimal("800.00"), true); // Dispara erro de saldo
        } catch (SaldoInsuficienteException e) {
            System.err.printf("⚠️  Interceptada Regra de Negócio: %s%n", e.getMessage());
        } catch (ContaInativaException e) {
            System.err.printf("🚨 Interceptada Restrição Cadastral: %s%n", e.getMessage());
        }

        System.out.println("================================================================================");
    }
}