Como objetos se relacionam (associação e composição) e como guardar e percorrer muitos objetos com arrays e com o Collections Framework.
Programação Orientada a Objetos em Java · Docente: Profa. Andréa Barboza Proto Sardi
Abertura. Saímos de "um objeto" para "muitos objetos colaborando". Dois temas: relacionamentos (como objetos se conectam) e coleções (como armazená-los). O projeto ganha uma turma de alunos.
Módulo 3 · Abertura
Objetivos de aprendizagem
🔗Relacionar
Distinguir associação de composição entre objetos.
📦Armazenar
Usar arrays e entender sua principal limitação.
📋Coletar
Trabalhar com List/ArrayList — o vetor dinâmico.
🔁Percorrer
Iterar coleções com for e com for-each, usando generics.
Objetivos. Dois blocos: relacionamentos e coleções. A motivação prática (uma turma) costura tudo. Generics aparece como o "tipo seguro" das coleções.
Retomada · Situação-problema
Um aluno não é uma turma
Já sabemos criar e proteger um Aluno. Mas uma escola tem centenas. Criar aluno1, aluno2, ... aluno300 é inviável.
💥
o problema
Precisamos de uma estrutura que guarde vários objetos e que cresça conforme a necessidade.
🎯Dois caminhos
O array (tamanho fixo, simples) e a coleção (tamanho dinâmico, poderosa). Veremos os dois.
Gancho. Ridicularize de leve a ideia de 300 variáveis. A necessidade de "guardar muitos" motiva arrays e, depois, coleções. Antes disso, vemos como os objetos se relacionam entre si.
Conceito 1
Associação: objetos que se conhecem
Um objeto guarda uma referência a outro como atributo. É uma relação do tipo "usa / tem um".
Livro.java
public classLivro {
String titulo;
Autor autor; // associação: Livro TEM UM Autor
}
🔗
a chave
O tipo de um atributo pode ser outra classe. Assim os objetos formam uma rede.
Associação. Retoma a colaboração do Módulo 1 (transferePara recebia uma Conta). Aqui o relacionamento é estrutural: o Livro tem um Autor como atributo. Outros exemplos: Aluno tem Curso; Pedido tem Cliente.
Conceito 2
Composição: relação parte-todo
Uma forma forte de associação: a parte não existe sem o todo. É um "é feito de".
🚗Carro é feito de Motor
O Motor é criado dentro do Carro. Destruiu o carro, foi-se o motor.
🏫Associação (mais fraca)
Um Professor dá aula numa Escola, mas existe independente dela.
🧩
teste prático
"Se eu destruir o todo, a parte deixa de fazer sentido?" Sim → composição. Não → associação.
Composição. Diferença de "vínculo de vida". Composição: parte criada e destruída com o todo (Carro/Motor, Pedido/ItemDePedido). Associação: vidas independentes. Em UML, composição é o losango preenchido. Não exagere no rigor — a intuição parte-todo basta agora.
Conceito 3 · vetor estático
Array: a primeira forma de guardar vários
sintaxe
Tipo[] nome = newTipo[3];
int[] numero = newint[3];
numero[0] = 10;
numero[1] = 20;
numero[2] = 30;
Posições contíguas, acessadas por índice (começa em 0).
Array. Sintaxe Tipo[] x = new Tipo[n]. Índice começa em 0; o último é n-1. Atenção ao ArrayIndexOutOfBounds. O array é a base; coleções vêm a seguir para superar suas limitações.
Vetor estático · uso
Percorrendo com for
Main.java
Integer[] numero = newInteger[3];
numero[0] = 1; numero[1] = 1; numero[2] = 1;
for (int i = 0; i < 3; i++) {
System.out.println(numero[i]);
}
📏
tamanho do array
numero.length dá o tamanho. O laço vai de 0 até length - 1.
?
Verificação
O que acontece se você acessar numero[3] num array de tamanho 3?
Percorrer. Resposta: ArrayIndexOutOfBoundsException — índices válidos são 0..2. Use .length em vez de "3" fixo para evitar bugs. Esse for clássico será simplificado pelo for-each adiante.
Situação-problema
A limitação do array: tamanho fixo
Ao criar new Tipo[3], o tamanho está travado. E se chegar um 4º aluno?
🔒Não cresce
Para adicionar mais, você teria que criar um array maior e copiar tudo na mão.
🛠️Trabalhoso
Remover do meio, buscar, reordenar — tudo manual. Muito código repetitivo.
💡
a saída
O Java já resolveu isso por nós: o Collections Framework, com estruturas que crescem sozinhas.
Limitação. "Arrays são trabalhosos" (fonte da disciplina). Tamanho fixo + operações manuais. Esse é o gancho perfeito para Collections: "não reinvente a roda — use a lista que o Java oferece".
Conceito 4
Collections Framework
Um conjunto de classes e interfaces do pacote java.util que representa estruturas de dados prontas e robustas.
📚Não reinvente a roda
Listas, conjuntos, mapas, filas — testados e otimizados, prontos para usar.
📋Começamos pela List
Uma lista permite elementos duplicados e mantém a ordem de inserção.
📦
import
Para usar: import java.util.ArrayList; (ou java.util.List) no topo do arquivo.
Collections. Reside em java.util desde o Java 2 (1.2). É a biblioteca padrão de estruturas de dados. Comece pela List por ser a mais intuitiva (parecida com array, mas dinâmica). Mencione import sem aprofundar pacotes (Módulo 5).
Conceito 5
List e a ArrayList
📜List (a interface)
Especifica o que uma lista deve saber fazer: adicionar, remover, buscar, contar.
⚡ArrayList (a implementação)
A mais usada. Usa um array interno que cresce sozinho. Rápida para buscar.
⚖️
ArrayList × LinkedList
ArrayList é mais rápida na busca; LinkedList, na inserção/remoção nas pontas.
List. Distinção interface × implementação (prepara o Módulo 4). List é o "contrato"; ArrayList é uma forma de cumpri-lo. Para o curso, ArrayList resolve quase tudo. A comparação com LinkedList é cultura geral por ora.
Vetor dinâmico · uso
Criar, adicionar, ler
Main.java
import java.util.ArrayList;
ArrayList<String> nomes = newArrayList<>();
nomes.add("maria");
nomes.add("joão");
System.out.println(nomes.size()); // 2System.out.println(nomes.get(0)); // maria
Método
Faz
add(x)
adiciona ao fim
get(i)
lê pelo índice
size()
quantos há
remove(i)
remove
ArrayList básico. Note o <> vazio na criação (inferência de tipo). add cresce a lista automaticamente — sem tamanho fixo! get/size substituem o índice e o .length do array. Mostre ao vivo adicionando um 3º nome sem "criar array maior".
Vetor dinâmico · operações
Buscar, conferir, remover
Main.java
nomes.indexOf("joão"); // → 1 (posição)
nomes.contains("maria"); // → true
nomes.add(0, "ana"); // insere no início
nomes.remove("joão"); // remove pelo valor
🧰
tudo pronto
Buscar, verificar presença, inserir em posição e remover — sem você escrever a lógica.
?
Atividade
Qual a diferença entre remove(0) e remove("ana")?
Operações. Resposta: remove(int) remove por índice; remove(Object) remove pela primeira ocorrência do valor — exemplo de sobrecarga (Módulo 2!). indexOf usa equals internamente — conecte com o trio de Object. contains devolve boolean.
Conceito 6
Generics: listas com tipo
O <Tipo> diz à lista o que ela guarda. O compilador garante: nada de tipo errado entra.
✅ArrayList<Aluno>
Só aceita Aluno. Ao tirar com get, já vem como Aluno — sem conversão manual.
⚠️ArrayList sem tipo
Aceita qualquer coisa. Você perde a segurança e precisa fazer casts arriscados.
🛡️
segurança de tipos
Generics transforma erros de tipo em erros de compilação (cedo), não de execução (tarde).
Generics. ArrayList<Pessoa> dos exercícios. Vantagem dupla: o compilador barra tipos errados e o get já devolve o tipo certo. Conecte com primitivos×wrappers do Módulo 1 (generics só aceita objetos → use Integer, não int).
Conceito 7
Percorrendo com for-each
🔢for tradicional
verboso
for (int i=0; i<lista.size(); i++){
System.out.println(lista.get(i));
}
✨for-each
limpo
for (Aluno a : turma) {
System.out.println(a.getNome());
}
📖
leia assim
"para cada Aluno a dentro de turma". Sem índices, sem erro de fronteira.
for-each. Sintaxe: for(Tipo elemento : colecao). Mais legível e sem off-by-one. Use quando só precisar ler cada elemento (não para remover durante a iteração). É o jeito idiomático de varrer coleções.
Projeto · Gestão Acadêmica
Uma turma de Alunos
Main.java
Aluno a1 = newAluno(); a1.nome = "Ana";
Aluno a2 = newAluno(); a2.nome = "Caio";
ArrayList<Aluno> turma = newArrayList<>();
turma.add(a1);
turma.add(a2);
for (Aluno a : turma) {
System.out.println(a.nome); // Ana / Caio
}
🎓
o sistema cresce
A turma agora comporta quantos alunos quiser — e percorrê-la é uma linha de for-each.
Turma. Marco do projeto evolutivo: de objetos isolados para uma coleção de objetos do nosso domínio. Junta tudo: classe Aluno (M1/M2), ArrayList<Aluno> (generics), for-each. A partir daqui dá para calcular média da turma, contar reprovados, etc.
Aplicação
Funciona com qualquer objeto
Main.java
Conta c1 = newConta(); c1.deposita(100);
Conta c2 = newConta(); c2.deposita(200);
ArrayList<Conta> contas = newArrayList<>();
contas.add(c1);
contas.add(c2);
for (Conta c : contas) c.exibeSaldo();
♻️
reuso
A mesma técnica serve para Alunos, Contas, Pessoas, Produtos... troca-se apenas o tipo entre < >.
Generalização. Mostra que coleções são agnósticas ao domínio — é o poder dos generics. Lista de Contas Correntes é exemplo das fontes. Reforce: o conhecimento se transfere para qualquer entidade.
Erros frequentes
Onde se tropeça
📏Index out of bounds
Acessar get(size()) ou array[length] — o último índice é tamanho − 1.
📦Esquecer o import
Usar ArrayList sem import java.util.ArrayList; → não compila.
🏷️Lista sem generics
Esquecer o <Tipo> e precisar de casts perigosos depois.
🔁Remover durante for-each
Alterar a lista enquanto a percorre com for-each → exceção de concorrência.
Erros. Os dois primeiros são imediatos para iniciantes. "Remover no for-each" é mais sutil (ConcurrentModificationException) — mencione e diga que se resolve com Iterator ou laço por índice reverso. Generics esquecido gera warnings que viram bugs.
Prática guiada
Média da turma
🎯Construir juntos
1. Crie 3 objetos Aluno com notas (use lerNota).
2. Adicione-os a uma ArrayList<Aluno> turma.
3. Com for-each, some as médias e imprima a média geral da turma.
🧮
dica
Use turma.size() como divisor — o código se adapta a qualquer quantidade de alunos.
Prática guiada. Integra M1+M2+M3: objetos, encapsulamento e coleção. O uso de size() como divisor mostra a vantagem da lista dinâmica (não precisa saber a quantidade de antemão). Faça ao vivo, peça previsões da saída.
Exercício autônomo
Sua vez: catálogo de Livros
📋Requisitos
• Classe Autor (nome) e classe Livro com associação a um Autor.
• Crie 3 livros (com autores) e guarde numa ArrayList<Livro>.
• Com for-each, imprima título + nome do autor de cada livro.
• Use contains ou indexOf para localizar um livro específico.
!
Entrega
Lista impressa + a posição de um livro buscado.
Exercício autônomo. Tempo: 20 min. Combina associação (Livro–Autor) com coleção. Reforça que o tipo de um atributo pode ser outra classe. Recolha; serve de base para herança (Livro/Autor poderiam compartilhar Pessoa, no Módulo 4).
Revisão · verificação
Revisão relâmpago
✓
Associação = "tem um"; composição = parte-todo.
✓
Array = tamanho fixo; ArrayList = cresce sozinho.
✓
Generics<Tipo> garante segurança de tipos.
✓
for-each percorre sem índices.
?
Questão 1
Qual a principal limitação do array que a ArrayList resolve?
?
Questão 2
Por que <Aluno> é melhor que uma lista sem tipo?
?
Questão 3
Quando usar composição em vez de associação?
Revisão. Respostas: (1) tamanho fixo → ArrayList cresce dinamicamente. (2) segurança de tipos em compilação + sem casts. (3) quando a parte não existe sem o todo. Ticket de saída.
Síntese do Módulo 3
O que levamos deste módulo
🔗Relacionamentos
Objetos se conectam por associação e composição.
📋Coleções
ArrayList + generics guardam muitos objetos com segurança.
🔁Iteração
for-each percorre tudo de forma limpa.
➡️
próximo módulo
Herança e Polimorfismo — Aluno e Professor são, no fundo, Pessoas. Vamos reaproveitar código com hierarquias.
Síntese. Relacionamentos + coleções + iteração. Ponte: percebemos repetição entre Aluno e Professor (ambos têm nome/idade) — herança elimina isso. Encerramento: "já agrupamos objetos; agora vamos organizá-los em famílias".