☕ 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:
- Integrar todos os pilares da POO em Java 17+ (Abstração, Encapsulamento, Herança e Polimorfismo).
- Modelar hierarquias de domínio usando Classes Abstratas, Interfaces e Enums.
- Gerenciar o estado da aplicação utilizando o Collections Framework (
List<T>,Map<K,V>) e realizar processamentos analíticos com a Stream API. - 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)
- Qual a finalidade da anotação
@Overrideacima do métodoexibirFicha()na classeAluno? - O que faz o método
Optional.ifPresentOrElse()introduzido no Java 9 e utilizado no exemplo acima?
🥈 Nível Prata (Aplicação)
- Adicione um método
double calcularMediaGeralTurma()na classeGestaoAcademicaServiceutilizando 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)
- Crie uma classe
Professorque herde dePessoae implemente a interfaceAutenticavel. Adicione umMap<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
- 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. - Executa a primeira função lambda se o
Optionalcontiver 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) ➡️