☕ Capítulo 20: Projeto Capstone Java 17+ POO — Sistema de Gestão Escolar com Collections e Persistência

🎯 Objetivos da Aula

Ao final desta aula, você será capaz de:

  1. Integrar todos os pilares da POO em Java 17+ (Abstração, Encapsulamento, Herança e Polimorfismo).
  2. Modelar hierarquias de domínio usando Classes Abstratas, Interfaces e Enums.
  3. Gerenciar o estado da aplicação utilizando o Collections Framework (List<T>, Map<K,V>) e realizar processamentos analíticos com a Stream API.
  4. Tratar condições de erro com Exceções Customizadas e implementar persistência em disco.

🧠 1. Arquitetura Orientada a Objetos do Projeto Capstone

A aplicação é dividida em classes de modelo de domínio, serviços de regras de negócio e camada de exibição CLI.

classDiagram
    class Pessoa {
        <<abstract>>
        #Long id
        #String nome
        #String email
        +getNome() String
        +exibirDados()* void
    }

    class Aluno {
        -String matricula
        -Double mediaGeral
        +getMatricula() String
        +getMediaGeral() Double
        +exibirDados() void
    }

    class Professor {
        -String especialidade
        -BigDecimal salario
        +exibirDados() void
    }

    class Autenticavel {
        <<interface>>
        +autenticar(String token) boolean
    }

    class AlunoService {
        -List~Aluno~ turma
        +cadastrar(aluno: Aluno) void
        +buscarPorMatricula(mat: String) Optional~Aluno~
        +listarAprovados() List~Aluno~
    }

    Pessoa <|-- Aluno : Herda
    Pessoa <|-- Professor : Herda
    Aluno ..|> Autenticavel : Implementa
    AlunoService --> Aluno : Gerencia

💻 2. Código Completo do Projeto Integrado (SistemaEscolar.java)

Let’s write the complete, executable Java 17+ code:

import java.util.*;
import java.util.stream.Collectors;
 
// 1. EXCEÇÃO CUSTOMIZADA DE NEGÓCIO
class RegraNegocioException extends Exception {
    public RegraNegocioException(String mensagem) {
        super(mensagem);
    }
}
 
// 2. CLASSE ABSTRATA BASE
abstract class Pessoa {
    private final Long id;
    private String nome;
    private String email;
 
    public Pessoa(Long id, String nome, String email) {
        this.id = id;
        this.nome = nome;
        this.email = email;
    }
 
    public Long getId() { return id; }
    public String getNome() { return nome; }
    public String getEmail() { return email; }
 
    public abstract void exibirFicha();
}
 
// 3. CLASSE ALUNO (HERANÇA)
class Aluno extends Pessoa {
    private final String matricula;
    private double mediaGeral;
 
    public Aluno(Long id, String nome, String email, String matricula, double mediaGeral) {
        super(id, nome, email);
        this.matricula = matricula;
        this.mediaGeral = mediaGeral;
    }
 
    public String getMatricula() { return matricula; }
    public double getMediaGeral() { return mediaGeral; }
 
    @Override
    public void exibirFicha() {
        System.out.printf(" [ALUNO] ID: %d | Matrícula: %-9s | Nome: %-20s | Média: %.2f%n",
                getId(), matricula, getNome(), mediaGeral);
    }
}
 
// 4. SERVIÇO DE GESTÃO ACADÊMICA COM STREAM API
class GestaoAcademicaService {
    private final List<Aluno> alunos = new ArrayList<>();
 
    public void cadastrarAluno(Aluno aluno) throws RegraNegocioException {
        if (aluno.getMediaGeral() < 0.0 || aluno.getMediaGeral() > 10.0) {
            throw new RegraNegocioException("A média geral deve estar entre 0.0 e 10.0.");
        }
        
        boolean matriculaExiste = alunos.stream()
                .anyMatch(a -> a.getMatricula().equals(aluno.getMatricula()));
                
        if (matriculaExiste) {
            throw new RegraNegocioException("Já existe um aluno cadastrado com a matrícula " + aluno.getMatricula());
        }
 
        alunos.add(aluno);
        System.out.println(">> Aluno '" + aluno.getNome() + "' cadastrado com sucesso!");
    }
 
    public Optional<Aluno> buscarPorMatricula(String matricula) {
        return alunos.stream()
                .filter(a -> a.getMatricula().equalsIgnoreCase(matricula))
                .findFirst();
    }
 
    public List<Aluno> listarAprovados(double mediaMinima) {
        return alunos.stream()
                .filter(a -> a.getMediaGeral() >= mediaMinima)
                .sorted(Comparator.comparingDouble(Aluno::getMediaGeral).reversed())
                .collect(Collectors.toList());
    }
 
    public void exibirTodos() {
        if (alunos.isEmpty()) {
            System.out.println(">> Nenhum aluno cadastrado.");
            return;
        }
        System.out.println("==========================================================================");
        alunos.forEach(Pessoa::exibirFicha);
        System.out.println("==========================================================================");
    }
}
 
// 5. CLASSE PRINCIPAL EXECUTÁVEL
public class SistemaEscolar {
    public static void main(String[] args) {
        GestaoAcademicaService service = new GestaoAcademicaService();
 
        System.out.println("==========================================================================");
        System.out.println("          SISTEMA DE GESTAO ACADEMICA (JAVA 17+ POO)                      ");
        System.out.println("==========================================================================");
 
        try {
            // Cadastrando alunos válidos
            service.cadastrarAluno(new Aluno(1L, "Carlos Silva", "carlos@email.com", "20261001", 8.50));
            service.cadastrarAluno(new Aluno(2L, "Ana Souza", "ana@email.com", "20261002", 9.40));
            service.cadastrarAluno(new Aluno(3L, "Bruno Lima", "bruno@email.com", "20261003", 6.80));
 
            System.out.println("\n--- LISTAGEM GERAL DE ALUNOS ---");
            service.exibirTodos();
 
            System.out.println("\n--- ALUNOS APROVADOS (MÉDIA >= 7.0 - ORDENADOS POR NOTA) ---");
            List<Aluno> aprovados = service.listarAprovados(7.0);
            aprovados.forEach(Aluno::exibirFicha);
 
            System.out.println("\n--- BUSCA POR MATRÍCULA (20261002) ---");
            Optional<Aluno> alunoBuscado = service.buscarPorMatricula("20261002");
            alunoBuscado.ifPresentOrElse(
                Aluno::exibirFicha,
                () -> System.out.println(">> Aluno não encontrado.")
            );
 
            // Testando validação de exceção
            System.out.println("\n--- TESTANDO VALIDAÇÃO DE REGRA DE NEGÓCIO ---");
            service.cadastrarAluno(new Aluno(4L, "Erro Teste", "erro@email.com", "20261004", 15.0));
 
        } catch (RegraNegocioException e) {
            System.err.println(">> EXCEÇÃO DE NEGÓCIO CAPTURADA: " + e.getMessage());
        }
    }
}

⚔️ 3. Desafios Práticos (Exercícios 30/50/20)

🥉 Nível Bronze (Fixação)

  1. Qual a finalidade da anotação @Override acima do método exibirFicha() na classe Aluno?
  2. O que faz o método Optional.ifPresentOrElse() introduzido no Java 9 e utilizado no exemplo acima?

🥈 Nível Prata (Aplicação)

  1. Adicione um método double calcularMediaGeralTurma() na classe GestaoAcademicaService utilizando a Stream API (alunos.stream().mapToDouble(...).average()) para calcular e retornar a média aritmética geral de todos os alunos cadastrados.

🥇 Nível Ouro (Desafio)

  1. Crie uma classe Professor que herde de Pessoa e implemente a interface Autenticavel. Adicione um Map<String, Pessoa> no serviço para permitir o armazenamento polimórfico de Alunos e Professores na mesma estrutura de dados.

💡 Gabaritos Sanfonados de Resposta

💡 Ver Gabarito do Nível Bronze
  1. Informa ao compilador Java que o método está sobrescrevendo a assinatura do método abstrato declarado na classe pai Pessoa. Se houver erro de digitação no nome ou parâmetros, o compilador gera um erro de compilação.
  2. Executa a primeira função lambda se o Optional contiver um valor presente, ou executa o segundo bloco de código (Runnable) caso o valor seja ausente (null).
💡 Ver Gabarito do Nível Prata & Ouro (Código Java Stream API)
public double calcularMediaGeralTurma() {
    return alunos.stream()
            .mapToDouble(Aluno::getMediaGeral)
            .average()
            .orElse(0.0);
}

📚 Referências Teóricas Oficiais

  • ORACLE JAVA DOCUMENTATION. Java SE 17 API Specification — java.util.stream.Stream & Collections.
  • MARTIN, Robert C.. Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall, 2008.

⬅️ Capítulo Anterior (Relacionamentos em Java) | Voltar ao Sumário da Fase 07 | Avançar para a Fase 08 (SQL & PostgreSQL) ➡️