- Aprender
- Computação e TI
- Programação
- Programação orientada a objetos com Java
- Objetos e referências em Java
Objetos, referências e igualdade em Java
null, passe objetos para métodos e retorne-os, e construa classes a partir de outros objetos e de listas de objetos. Depois, escreva equals e hashCode para que listas, conjuntos e mapas comparem seus objetos pelo conteúdo, e mantenha a interface textual de um programa separada de sua lógica.BásicoAula 6 de 11 do curso Curso de Programação orientada a objetos com Java
O que você vai aprender
- Explique o que uma variável Java armazena para tipos primitivos e de referência, preveja o efeito da atribuição em cada caso e trabalhe com variáveis que não se referem a nenhum objeto.
- Compare variáveis de tipos primitivos e de referênciaNomeie os tipos primitivos de Java, explique que uma variável de referência armazena uma referência a um objeto e preveja o efeito de copiar cada tipo de variável.
- Acompanhe referências ao mesmo objeto, reatribuições e objetos inalcançáveisAcompanhe várias variáveis que se referem a um objeto, distinga a alteração de um objeto da alteração da referência de uma variável e explique o que acontece com objetos aos quais nada se refere.
- Trabalhe com segurança com a referência nulaExplique o significado de null, reconheça onde ele aparece, evite NullPointerException e decida quando null é um valor adequado.
- Compare variáveis de tipos primitivos e de referência
- Explique como Java passa argumentos, escreva métodos que recebem e alteram objetos ou os comparam com outro objeto da mesma classe e retorne objetos novos ou existentes com segurança.
- Use objetos como parâmetros de métodosPreveja o que um método pode ou não alterar ao receber um valor primitivo ou uma referência e escreva métodos que recebem objetos de outra classe ou de sua própria classe.
- Retorne objetos de métodosEscreva métodos que criam e retornam novos objetos, retornam referências a objetos que já existem e evitam expor acidentalmente o estado interno de um objeto.
- Use objetos como parâmetros de métodos
- Projete classes cujos campos são outros objetos ou listas de objetos, trabalhe com listas que armazenam objetos e decida quando objetos contidos devem ser compartilhados ou copiados.
- Componha uma classe com outros objetosModele relações de posse com campos de tipo de referência, delegue tarefas aos objetos contidos e decida se um objeto contido deve ser compartilhado ou copiado.
- Armazene e processe objetos em listasAdicione, encontre, altere e remova objetos em um ArrayList e explique por que a lista armazena referências em vez de cópias.
- Desenvolva uma classe em torno de uma lista de objetosEscreva uma classe que mantenha uma lista de objetos como campo, inicialize-a, ofereça métodos para trabalhar com seu conteúdo e mantenha a própria lista oculta.
- Componha uma classe com outros objetos
- Distinga identidade de igualdade de conteúdo, escreva métodos equals e hashCode corretos e preveja como listas, conjuntos e mapas os usam.
- Distinga identidade de igualdade de conteúdoExplique o que == e equals comparam em tipos primitivos, cadeias de caracteres, objetos empacotadores e suas próprias classes, e preveja o resultado de cada comparação.
- Escreva um método equalsSobrescreva equals com a assinatura e as etapas corretas, compare cada tipo de campo corretamente e mantenha o método consistente com seu contrato geral.
- Implemente hashCode em conjunto com equalsExplique o contrato de hashCode, escreva hashCode para uma classe que sobrescreve equals e preveja como HashSet e HashMap se comportam com e sem esse método.
- Distinga identidade de igualdade de conteúdo
- Estruture um programa textual de modo que uma classe converse com o usuário e outras classes mantenham os dados e as regras, depois amplie-o e verifique-o sem misturar as duas partes.
- Divida um programa em interface do usuário e classes de lógicaDecida qual código pertence à interface do usuário e qual pertence à lógica, e conecte-os por meio de parâmetros de construtor e um método de início.
- Amplie e verifique um programa com interface e lógica separadasAdicione recursos ampliando a lógica e chamando-a da interface, verifique a lógica diretamente com valores conhecidos e mantenha cada classe focada em uma tarefa.
- Divida um programa em interface do usuário e classes de lógica
Guia de estudo de Objetos e referências em Java
1Acompanhe valores e referências
Uma variável e um objeto são partes diferentes de um programa Java. Compreender o que cada variável armazena permite explicar por que uma atribuição cria números independentes enquanto outra dá a dois nomes acesso ao mesmo objeto. Comece pelos valores copiados e acompanhe as alterações uma instrução por vez. A mesma representação ajudará você a encontrar objetos ausentes, interpretar erros de null e decidir se uma alteração deve ser compartilhada.
1.1 O que uma variável armazena
Uma variável Java armazena um valor. A questão importante é se esse valor é um dado comum ou uma referência que aponta para um objeto. A atribuição copia esse valor nos dois casos.
Dados primitivos e referências a objetos
Java tem oito tipos primitivos: byte, short, int, long, float, double, char e boolean. Uma variável de tipo primitivo armazena seu dado diretamente. Uma variável de referência armazena uma referência a um objeto ou o valor especial null. Classes como String, Scanner e ArrayList, suas próprias classes e vetores são tipos de referência.
| Declaração | O que a variável armazena |
|---|---|
int count = 5; | O valor inteiro 5 |
Counter c = new Counter(); | Uma referência a um novo contador |
Counter c = null; | Nenhuma referência a objeto |
Em new Counter(), Java cria um objeto, executa seu construtor e produz uma referência. A atribuição armazena essa referência em c. Desenhe a variável como uma caixa com uma seta que aponta para um objeto separado. Cada avaliação de new cria um objeto separado, mesmo quando os argumentos são iguais.
Copie o valor e acompanhe o resultado
Com tipos primitivos, alterar a variável copiada deixa a primeira variável inalterada:
int a = 5;
int b = a;
b = b + 1;
A última linha lê 5 de b, soma 1 e armazena 6 em b. O valor em a permanece 5.
Para um exemplo com referência, use esta pequena classe:
class Counter {
private int value;
Counter() { this.value = 0; }
Counter(int value) { this.value = value; }
void increase() { value++; }
int value() { return value; }
}
Agora acompanhe estas instruções:
Counter first = new Counter();
Counter second = first;
second.increase();
System.out.println(first.value()); // 1
Apenas a primeira linha cria um contador. A segunda linha copia a referência, então a chamada de método altera o único contador que ambas as variáveis alcançam. Para criar um contador independente com os mesmos dados, escreva new Counter(first.value()). Copiar uma variável não chama um construtor.
Classes empacotadoras e strings imutáveis
Cada tipo primitivo tem uma classe empacotadora, como Integer, Double, Boolean ou Character. Variáveis de tipos empacotadores armazenam referências ou null. Java pode converter automaticamente entre um tipo primitivo e seu tipo empacotador; essas conversões são empacotamento automático e desempacotamento. Listas armazenam referências, então uma lista de inteiros é ArrayList<Integer>.
String também é um tipo de referência, mas seus objetos são imutáveis: seus caracteres não podem mudar após a criação.
String s = "java";
String t = s;
t = t.toUpperCase();
Aqui, s ainda se refere a “java” e t se refere a “JAVA”. A última linha atribui o resultado a t; ela não altera a cadeia de caracteres original compartilhada. Métodos de String podem reutilizar o objeto original quando nenhuma alteração é necessária.
Exibir um objeto usa seu método toString(). Sem uma sobrescrita, o resultado herdado tem uma forma como Counter@7e9e5f8a: nome da classe, @ e um código de dispersão hexadecimal. Defina toString() quando a saída exibida deva mostrar os dados do objeto.
Principais conclusões
- A atribuição copia um valor: um dado primitivo ou uma referência a objeto.
- Cada avaliação de
newcria um objeto separado. - Copiar uma referência compartilha o objeto; não copia seus campos.
- Tipos empacotadores e strings são tipos de referência; strings são imutáveis.
- Exibir um objeto usa
toString().
1.2 Objetos compartilhados e alteração de setas
Duas variáveis que se referem ao mesmo objeto são apelidos de referência. Um apelido oferece outro caminho até dados existentes. Para acompanhar um programa, mantenha as variáveis separadas dos objetos que elas alcançam.
A mutação altera dados; a reatribuição altera uma variável
Uma mutação altera o estado de um objeto, como seu peso armazenado. Uma reatribuição coloca um valor diferente em uma variável. Para uma variável de referência, isso muda sua seta.
Use uma caixa cujo peso é dado em quilogramas:
class Box {
private double weight;
Box(double weight) { this.weight = weight; }
double getWeight() { return weight; }
void setWeight(double weight) { this.weight = weight; }
}
Acompanhe esta sequência:
Box a = new Box(2);
Box b = a;
b.setWeight(5);
b = new Box(7);
- Desenhe a variável
aapontando para o objeto A, com peso 2. - Desenhe
bapontando para esse mesmo objeto A. Ainda há uma caixa. - Altere o peso dentro do objeto A para 5. Ambos os caminhos agora chegam ao peso 5.
- Crie o objeto B com peso 7 e redirecione apenas
bpara ele.
Ao final, a.getWeight() é 5 e b.getWeight() é 7. A quarta instrução não reinicia o objeto A nem move a para o objeto B.
| Ação | Alteração no diagrama | Efeito por meio de outros apelidos |
|---|---|---|
b.setWeight(5) | Altera um campo dentro do objeto | Eles veem o novo peso |
b = new Box(7) | Redireciona a seta de b | Eles mantêm suas setas existentes |
b = null | Remove a seta de b | Eles ainda alcançam seus objetos |
A alcançabilidade controla o tempo de vida de um objeto
Um objeto está alcançável enquanto o programa em execução puder seguir alguma cadeia utilizável de referências até ele. Essa cadeia pode passar por uma variável, um campo de objeto ou um elemento de lista. Portanto, o desaparecimento de uma variável local não é suficiente para tornar um objeto inalcançável se uma lista ainda o armazenar.
Continue o rastreamento com a = null;. O objeto A, que pesa 5 kg, agora não tem nenhum caminho alcançável neste exemplo. Ele está disponível para coleta de lixo. Java recupera automaticamente a memória de objetos inalcançáveis, mas o coletor escolhe quando fazê-lo; a atribuição não garante coleta imediata. O objeto B permanece alcançável por meio de b.
Você não libera objetos Java manualmente. Quando um programa perde sua última referência utilizável a um objeto, também perde a capacidade de inspecionar ou alterar os dados desse objeto.
Encontre os apelidos ocultos
Apelidos surgem quando um objeto é adicionado a uma lista, passado a um método, retornado de um método ou armazenado no campo de outro objeto. Cada operação pode passar uma referência adiante. Antes de alterar um objeto recebido de outro lugar, identifique quais outros trechos de código ainda podem alcançá-lo.
Um erro comum começa com o pensamento: “Copiei a variável, então tenho minha própria caixa.” Use um objeto novo quando as alterações precisarem ser independentes. Para diagnosticar o erro, conte as criações de objetos separadamente das atribuições, identifique cada objeto uma vez e processe cada instrução em ordem. Não redesenhe um objeto só porque uma segunda variável agora aponta para ele.
Principais conclusões
- Apelidos apontam para um único objeto, portanto compartilham suas mutações.
- A reatribuição redireciona uma variável e deixa os outros apelidos inalterados.
- Acompanhe os campos e as setas de referência separadamente.
- Um objeto inalcançável está disponível para coleta de lixo; o momento não é especificado.
1.3 Nenhum objeto: null e verificações seguras
O valor null significa que uma referência não aponta para nenhum objeto. Ele é útil para um resultado ausente, mas um programa deve tratar essa ausência antes de tentar usar um objeto.
Ausente é diferente de vazio
String text = ""; refere-se a uma cadeia de caracteres real de comprimento 0. String text = null; não se refere a nenhuma cadeia, então não há objeto no qual chamar length().
Um tipo primitivo não pode armazenar null: int x = null; não compila. Um tipo empacotador pode armazená-lo: Integer x = null; compila. Converter esse valor empacotado em um tipo primitivo com int y = x; lança uma NullPointerException, pois o desempacotamento precisa de um objeto empacotador.
Campos recebem valores padrão iniciais quando um objeto é criado. Campos de referência começam como null, campos numéricos como 0 e campos booleanos como false, até que a inicialização lhes atribua outros valores. Variáveis locais não têm valor padrão. Ler uma variável local antes de lhe atribuir um valor causa um erro de compilação, como “a variável pode não ter sido inicializada”.
Encontre a referência ausente
Chamar um método de instância por meio de null, acessar um campo de instância por meio dele ou ler o comprimento de um vetor por meio dele lança NullPointerException em tempo de execução. Por exemplo:
String name = null;
System.out.println(name.length());
Leia o número da linha da exceção e identifique qual referência nessa linha estava ausente. Mensagens explicativas são ativadas por padrão a partir do Java 15. Uma mensagem pode dizer Cannot invoke "SimpleDate.getYear()" because "this.birthday" is null. Nomes locais podem aparecer como marcadores, como <local4>, dependendo dos detalhes da compilação. Acompanhe o código de volta até o construtor, a atribuição ou a busca que deveria ter fornecido o objeto.
Exibir uma variável String cujo valor é null exibe “null”; concatená-la produz um texto como “Hi null”. Essas operações não precisam chamar um método de instância por meio dessa variável. Elas podem revelar dados ausentes antes que uma chamada de método posterior falhe.
Verifique antes de usar
O operador && usa avaliação de curto-circuito: ele avalia o lado direito apenas se o lado esquerdo for verdadeiro.
if (name != null && name.length() > 0) {
System.out.println(name);
}
Quando name é null, o primeiro teste é falso e length() é ignorado. Inverter os dois testes elimina essa proteção: a chamada de método seria executada primeiro.
| Finalidade | Expressão segura |
|---|---|
| Verificar ausência | name == null |
| Verificar presença | name != null |
| Comparar com um comando fixo | "quit".equals(command) |
A cadeia de caracteres fixa é um objeto conhecido, então seu equals pode receber null e retornar falso. command.equals("quit") falha quando command é null. Da mesma forma, x.equals(null) não é uma maneira de verificar se x existe: ela já exige um objeto.
Faça da ausência uma decisão clara de projeto
Inicialize um campo de lista com new ArrayList<>() para que uma coleção vazia esteja disponível imediatamente. Se uma busca retornar null para “não encontrado”, documente esse resultado e verifique-o no local da chamada. Evite preencher todos os campos com objetos arbitrários apenas para silenciar uma exceção: forneça o objeto de que o programa precisa ou dê à ausência um significado claro que o código que chama o método trate.
Principais conclusões
nullsignifica ausência de objeto; uma cadeia de caracteres vazia é um objeto utilizável.- Campos recebem valores padrão, mas variáveis locais precisam receber valores antes de serem lidas.
- Desreferenciar null e desempacotar um objeto empacotador nulo causam falhas em tempo de execução.
- Coloque a verificação de null antes do acesso ao membro em
&&. - Chame comparações com texto fixo na cadeia de caracteres conhecida.
- Inicialize coleções e trate deliberadamente resultados ausentes de buscas.
2Use objetos em chamadas de métodos
Métodos permitem que objetos trabalhem juntos. Uma chamada pode ler ou alterar um objeto compartilhado, criar um resultado independente ou retornar um objeto já armazenado em outro lugar. Essas escolhas afetam o que quem chamou o método vê depois da chamada. Acompanhe explicitamente as referências dos parâmetros e dos retornos e coloque as comparações na classe que contém os dados. Depois, decida quais métodos devem compartilhar o estado existente e quais devem protegê-lo com uma cópia.
2.1 Parâmetros recebem valores copiados
Java passa todos os argumentos por valor. Um parâmetro é uma nova variável que recebe uma cópia do valor do argumento. Para um argumento que é um objeto, o valor copiado é uma referência.
Alterar o parâmetro ou alterar o objeto?
Um parâmetro de tipo primitivo tem seus próprios dados:
static void addTen(int n) {
n = n + 10;
}
Se x for 5, chamar addTen(x) deixa x em 5. Apenas o parâmetro local n passa a ser 15.
Um parâmetro de referência pode alcançar o mesmo objeto que quem chamou o método. Suponha que Person.growOlder() aumente a idade armazenada:
static void celebrate(Person p) {
p.growOlder();
}
static void replace(Person p) {
p = new Person("Bo", 30);
}
Durante celebrate(ana), o parâmetro p e a variável ana são apelidos de referência. A idade alterada permanece visível depois. Durante replace(ana), atribuir um valor a p redireciona apenas esse parâmetro. A variável ana de quem chamou o método continua se referindo à pessoa original. Um método que troca dois parâmetros de referência tem a mesma limitação: ele troca suas próprias cópias, não as variáveis de quem o chamou.
| Dentro de um método | Quem o chamou observa |
|---|---|
| Atribuir um valor a um parâmetro primitivo | Nenhuma alteração na variável de quem chamou |
| Alterar o objeto do parâmetro | Estado alterado do objeto compartilhado |
| Reatribuir um parâmetro de referência | Nenhuma alteração na variável de quem chamou |
Receba um objeto de outra classe
Um controle de entrada pode ler a altura de uma pessoa e manter sua própria contagem de verificações. Esta classe espera uma pessoa não nula com um método getHeight() que retorna centímetros:
class HeightGate {
private int minHeight;
private int checks;
HeightGate(int minHeight) {
this.minHeight = minHeight;
}
boolean allowIn(Person p) {
checks++;
return p.getHeight() >= minHeight;
}
int checks() { return checks; }
}
Para um controle configurado para 120 cm, verificar pessoas de 132 cm e 115 cm retorna verdadeiro e falso. O controle informa então 2 verificações. Ele lê a altura da pessoa por meio de um método público e altera seu próprio contador.
Receba outro objeto da mesma classe
Dentro de SimpleDate.before(SimpleDate other), this é a data que recebe a chamada e other é o argumento. Uma classe pode ler os campos privados de qualquer objeto dessa mesma classe.
Para os campos day, month e year, compare nesta ordem:
boolean before(SimpleDate other) {
if (year != other.year) {
return year < other.year;
}
if (month != other.month) {
return month < other.month;
}
return day < other.day;
}
Para 14 de março de 2001 e 2 de novembro de 2001, os anos coincidem e o mês 3 é menor que o mês 11, então a primeira data é anterior. Datas iguais retornam falso porque nenhuma é anterior à outra.
Person.olderThan(other) pode delegar a birthday.before(other.birthday). A comparação de datas pertence à classe que contém os dados da data. Métodos que chamam métodos de instância em um parâmetro precisam de um argumento não nulo; verifique-o ou inclua essa exigência na documentação de uso do método.
Principais conclusões
- Todo parâmetro recebe um valor copiado, incluindo parâmetros de referência.
- Uma mutação pode afetar um objeto compartilhado; a reatribuição de parâmetro não pode substituir a variável de quem chamou o método.
thisidentifica o objeto receptor; um parâmetro de objeto identifica o argumento.- Uma classe pode acessar campos privados em outro objeto da mesma classe.
- Delegue comparações à classe que contém os dados relevantes.
2.2 Retorno de um objeto novo ou existente
Um método que retorna um objeto retorna uma referência. Seu tipo informa que tipo de objeto pode ser retornado, mas não informa se o objeto é novo, compartilhado ou ausente. A finalidade do método deve deixar essa escolha clara.
Crie um resultado
Um método fábrica fornece um objeto. Ele pode criar um ou reutilizar um existente. Aqui, cada chamada cria deliberadamente um tíquete separado:
class TicketMachine {
private int nextNumber = 1;
Ticket issue() {
Ticket t = new Ticket(nextNumber);
nextNumber++;
return t;
}
}
A máquina espera um construtor Ticket que armazene seu número. Três chamadas criam três tíquetes numerados 1, 2 e 3. Retornar uma variável local é seguro: sua referência é copiada para quem chamou o método, que pode manter o objeto alcançável depois que a variável local sai de escopo.
Um método de cópia promete um objeto independente. Na classe Counter:
Counter copy() {
return new Counter(this.value);
}
Após Counter a = new Counter(4); Counter b = a.copy(); b.increase();, a.value() é 4 e b.value() é 5. Substituir a.copy() por a compartilharia o contador.
Um método também pode construir um objeto calculado. Para um Point com campos double x e y:
Point midpoint(Point other) {
return new Point((x + other.x) / 2.0,
(y + other.y) / 2.0);
}
Os pontos (2, 4) e (6, 10) resultam em (4,0; 7,0). O método calcula cada coordenada e passa ambas a um novo ponto; nenhum dos pontos originais muda.
Retorne um objeto já armazenado
O findByName de um cadastro deve retornar a pessoa armazenada correspondente se quem chama o método precisar trabalhar com essa entrada. Criar new Person(name, 0) retornaria dados substitutos inventados e desvincularia alterações posteriores do cadastro.
Uma busca pode retornar null quando não há correspondência:
Person p = register.findByName("Lin");
if (p != null) {
System.out.println(p);
} else {
System.out.println("Not found");
}
Se p for a pessoa armazenada, alterações por meio de p também alteram a pessoa alcançada pelo cadastro. Uma instrução return não copia o objeto.
Mantenha o estado interno mutável sob controle
Suponha que Person.getBirthday() retorne seu campo SimpleDate e que essa data tenha métodos modificadores. Quem chama o método pode então alterar a data de nascimento da pessoa sem chamar um método de Person. Isso é uma exposição de estado mutável.
Para proteger o campo, retorne uma nova data construída a partir de seus valores:
SimpleDate getBirthday() {
return new SimpleDate(birthday.getDay(),
birthday.getMonth(),
birthday.getYear());
}
Essa versão exige que um objeto de data de nascimento tenha sido inicializado. Outros projetos usam uma data imutável ou oferecem consultas específicas sem um método de acesso à data. Escolha o comportamento de cada método: find normalmente compartilha uma entrada armazenada, enquanto copy cria independência. O nome curto de um método de acesso, por si só, não garante proteção.
Principais conclusões
- O retorno de um objeto passa uma referência a quem chamou o método.
- Métodos fábrica podem criar ou reutilizar objetos; um método de cópia cria um objeto separado.
- Métodos de busca podem compartilhar entradas existentes e devem definir um resultado para a ausência de correspondência.
- Retornar um campo mutável pode permitir que quem chamou o método contorne os métodos do proprietário.
3Conecte objetos e listas
Um modelo maior é uma rede de objetos: uma pessoa tem uma data, uma playlist tem uma lista e a lista armazena músicas. Cada conexão é uma referência com consequências para o compartilhamento e as alterações. Construa essas conexões deliberadamente, delegue tarefas ao objeto que sabe realizá-las e mantenha as regras da coleção por trás de métodos úteis. Uma cópia do contêiner pode proteger a composição da lista e ainda compartilhar todos os objetos dentro dela.
3.1 Objetos com campos de tipo de referência
Composição constrói uma classe usando campos cujos tipos são outras classes. Ela expressa uma relação de posse: uma pessoa tem uma data de nascimento; um curso tem um professor. O objeto externo armazena uma referência ao objeto contido.
Crie e conecte os objetos
Um construtor pode receber uma data existente:
class Person {
private String name;
private SimpleDate birthday;
Person(String name, SimpleDate birthday) {
this.name = name;
this.birthday = birthday;
}
boolean olderThan(Person other) {
return birthday.before(other.birthday);
}
public String toString() {
return name + ", born " + birthday;
}
}
Chamar new Person("Ana", new SimpleDate(14, 3, 2001)) primeiro cria uma data e depois fornece sua referência à pessoa. Outra opção é um construtor de pessoa receber dia, mês e ano e criar a data dentro de seu corpo. Com essa abordagem, quem chama o construtor não mantém uma referência à data recém-criada.
Delegue tarefas ao objeto contido
A chamada birthday.before(other.birthday) pede ao objeto de data que compare datas. Person não precisa repetir as comparações de ano, mês e dia. A concatenação em toString() também delega: Java chama o toString() do objeto de data de nascimento para obter seu texto.
Um objeto de data pode ajudar a calcular a idade. Comece com o ano atual menos o ano de nascimento. Depois, forme a data do aniversário deste ano e subtraia um se a data atual vier antes dela. Uma pessoa nascida em 14 de março de 2001 tem 24 anos em 1 de janeiro de 2026: a diferença entre os anos é 25, mas o aniversário ainda não chegou. Em 14 de março de 2026, o aniversário já chegou e a idade é 25. Manter a comparação de datas em SimpleDate evita reproduzir suas regras em cada método de pessoa.
Decida se o objeto interno é compartilhado
O construtor acima armazena seu argumento diretamente. Se duas pessoas receberem um SimpleDate mutável, ambos os campos de data de nascimento apontarão para a mesma data. Alterar essa data afeta as duas pessoas.
| Escolha do construtor | Consequência para as referências |
|---|---|
| Armazenar a referência à data recebida | Quem chamou o construtor e a pessoa podem compartilhar a data |
| Construir uma nova data a partir dos valores recebidos | A pessoa começa com sua própria data |
| Armazenar uma data imutável | O compartilhamento não pode alterar os valores da data |
Um construtor defensivo pode copiar o dia, o mês e o ano da data recebida. Uma data imutável não tem métodos que alterem seu estado e mantém seus valores fixos; campos primitivos simples podem ser final. Copiar na entrada é importante mesmo que um método de acesso retorne uma cópia depois, pois quem chamou o construtor originalmente ainda pode ter o objeto recebido.
Trate conexões opcionais
Um Course pode ainda não ter professor. Se esse for um estado significativo, teacherName() pode verificar o campo e retornar “ainda sem professor”. Caso contrário, exija um professor na construção.
Em course.getTeacher().getName(), cada chamada de instância precisa de um objeto: um curso nulo falha em getTeacher(), e um professor nulo falha em getName(). Um método no curso pode manter essa verificação de ausência em um só lugar e evitar que todos os chamadores precisem conhecer a mesma cadeia.
Principais conclusões
- A composição conecta um objeto externo a outros objetos por meio de campos.
- Delegue operações ao objeto que contém os dados relevantes.
- Armazenar uma referência mutável recebida pode compartilhar o estado com quem chamou o construtor.
- Cópias ou objetos internos imutáveis podem proteger um estado independente.
- Campos de objetos opcionais precisam de uma política clara de tratamento de null.
3.2 Listas armazenam referências a seus elementos
Um ArrayList<Person> é um objeto cujos elementos armazenam referências a pessoas. A lista controla sua sequência de entradas; cada pessoa controla seus próprios campos. São tipos separados de alteração.
Adicione um objeto e alcance-o novamente
ArrayList<Person> people = new ArrayList<>();
people.add(ana);
people.get(0).growOlder();
Esse código precisa de import java.util.ArrayList; e de uma pessoa existente e não nula ana. add copia a referência. A chamada growOlder() alcança a mesma pessoa que ana, então a alteração da idade fica visível pelos dois caminhos. Aqui, a lista nunca cria uma segunda pessoa.
Você pode criar um objeto ao adicioná-lo: people.add(new Person("Lin", 1998));, usando um construtor de pessoa que aceita um nome e um ano de nascimento. A lista fornece então ao programa um caminho até essa nova pessoa por meio de get ou de um laço. Argumentos de tipo de listas devem ser tipos de referência, então use ArrayList<Integer> para números inteiros.
Calcule a partir de objetos e selecione um objeto
Suponha que uma lista armazene pacotes com pesos de 2,5 kg, 4,0 kg e 1,5 kg. Um laço soma o peso de cada pacote:
double total = 0;
for (Parcel p : parcels) {
total += p.getWeight();
}
As somas acumuladas são 2,5, 6,5 e 8,0 kg. Selecionar o pacote mais pesado exige uma referência e uma comparação:
Parcel heaviest = parcels.get(0);
for (Parcel p : parcels) {
if (p.getWeight() > heaviest.getWeight()) {
heaviest = p;
}
}
Essa seleção exige uma lista não vazia. O resultado é o objeto do pacote de 4,0 kg, então quem chama o método também pode usar seus outros métodos. Para uma lista vazia, decida qual será o resultado de ausência antes de ler o elemento 0.
Busque em todas as entradas
Uma busca por nome em uma classe que armazena people pode usar:
Person findByName(String name) {
for (Person p : people) {
if (p.getName().equals(name)) {
return p;
}
}
return null;
}
Essa versão espera pessoas não nulas com nomes não nulos. A primeira correspondência é retornada imediatamente. O return null final pertence ao trecho após o laço: uma comparação sem correspondência na primeira pessoa não exclui correspondências posteriores.
contains, indexOf e remove(Object) fazem buscas usando equals. Se Person herdar a igualdade baseada em identidade, uma pessoa recém-criada não corresponderá a uma existente só porque seus campos coincidem.
Remova uma entrada sem destruir um objeto
Um objeto pode aparecer duas vezes em uma lista ou em várias listas. Remover uma entrada não destrói o objeto nem remove outras referências a ele.
Não remova entradas diretamente da mesma lista durante um percurso for-each. Isso pode lançar ConcurrentModificationException. Reúna os candidatos primeiro e depois remova-os:
ArrayList<Person> toRemove = new ArrayList<>();
for (Person p : people) {
if (p.getName().equals("Lin")) {
toRemove.add(p);
}
}
people.removeAll(toRemove);
O percurso termina antes que a composição da lista mude. removeAll usa igualdade, então remove entradas iguais aos candidatos reunidos.
Principais conclusões
- Adicionar um objeto a uma lista compartilha uma referência a ele.
- Um laço pode calcular um total ou manter uma referência a um objeto selecionado.
- Uma busca sem correspondência só retorna após todos os candidatos serem verificados.
- Operações de pertinência em listas dependem de
equals. - Remover uma entrada deixa outras referências intactas.
- Reúna primeiro os elementos a remover e altere a lista após o percurso.
3.3 Uma classe proprietária de uma lista
Uma classe pode manter uma lista em um campo privado e oferecer operações que descrevam sua finalidade. Uma playlist armazena músicas, conta-as e calcula sua duração. Quem chama seus métodos não deve precisar gerenciar a lista diretamente.
Inicialize e encapsule a coleção
Esta playlist espera objetos Song não nulos com um método seconds():
import java.util.ArrayList;
class Playlist {
private String name;
private ArrayList<Song> songs;
Playlist(String name) {
this.name = name;
this.songs = new ArrayList<>();
}
void add(Song song) { songs.add(song); }
int count() { return songs.size(); }
int totalSeconds() {
int total = 0;
for (Song song : songs) {
total += song.seconds();
}
return total;
}
}
Sem new ArrayList<>(), o campo de referência permanece null; a primeira chamada a add falha. Uma lista vazia já é um objeto utilizável, com tamanho 0.
Para músicas com duração de 210 s, 185 s e 247 s, count() retorna 3. O total cresce de 210 para 395 e depois para 642 s. A divisão inteira por 60 resulta em 10 minutos, com resto de 42 segundos. Um método longest() pode selecionar e retornar a música de 247 s, usando o mesmo padrão de seleção de referência do pacote mais pesado. Defina um resultado de ausência para uma playlist vazia.
Proteja a composição da lista por meio de métodos
Manter songs privado bloqueia o acesso direto ao campo, mas return songs; expõe essa própria lista. Quem chama o método poderia chamar clear() e contornar todas as regras da playlist.
Operações específicas como add, count e totalSeconds mantêm a responsabilidade com a playlist. Seu método add poderia impor um tamanho máximo ou recusar duplicatas com songs.contains(song). Verificações de duplicidade baseadas em conteúdo precisam que o equals da classe de música defina quais campos tornam duas músicas iguais.
Quando um construtor receber uma lista inicial, copie sua estrutura com this.songs = new ArrayList<>(initialSongs);. Armazenar o parâmetro diretamente permitiria que quem chamou o construtor mantivesse um apelido de referência à lista interna da playlist.
Uma nova lista ainda pode compartilhar músicas
Uma consulta pode retornar new ArrayList<>(songs). Isso é uma cópia superficial: um novo objeto de lista que contém as referências existentes às músicas.
| Ação de quem chamou o método na cópia retornada | Efeito na playlist |
|---|---|
| Remover uma entrada | A composição da playlist permanece igual |
| Esvaziar a cópia | A composição da playlist permanece igual |
| Alterar uma música compartilhada | A playlist vê o estado alterado dessa música |
Uma consulta songsLongerThan(200) cria uma nova lista de resultados, percorre as músicas armazenadas e adiciona aquelas cuja duração excede 200 s. Para as três músicas acima, ela retorna as músicas de 210 s e 247 s. Quando os valores das músicas também precisarem ser independentes, copie as músicas mutáveis ou torne Song imutável.
Principais conclusões
- Um objeto proprietário, sua lista e os elementos dela são objetos separados.
- Inicialize a lista antes de usá-la.
- Ofereça operações que imponham as regras da coleção do proprietário.
- Copie listas recebidas para evitar um apelido externo à lista interna.
- Uma cópia superficial de lista compartilha os objetos de seus elementos.
4Defina o que torna objetos iguais
Dois objetos podem descrever a mesma coisa e ainda ser objetos separados. Java oferece testes diferentes para identidade e conteúdo, e as coleções dependem da escolha feita por sua classe. Comece escolhendo os campos que definem a igualdade e depois implemente equals e hashCode em conjunto. Seus contratos explicam por que uma lista pode encontrar um objeto igual, por que uma colisão de hash é permitida e por que alterar uma chave armazenada pode prejudicar uma consulta.
4.1 Identidade e conteúdo correspondente
Identidade verifica se duas referências apontam para o mesmo objeto. Igualdade de conteúdo verifica se objetos têm os mesmos dados relevantes, de acordo com as regras da classe. Dois objetos de livro separados podem descrever o mesmo título e ano de publicação sem serem idênticos.
Escolha a comparação adequada
| Valores comparados | Significado de == | Significado de equals |
|---|---|---|
| Primitivos | Compara valores primitivos | Um tipo primitivo não tem método de instância |
| Referências | Mesmo objeto, ou ambos null | Regra de igualdade da classe |
| Strings | Mesmo objeto de cadeia de caracteres | Conteúdo de caracteres correspondente |
Classe que herda Object.equals | Mesmo objeto | Mesmo objeto |
Toda classe herda equals(Object) de Object. Essa versão padrão verifica a identidade. Java não inspeciona automaticamente todos os campos de uma nova classe.
String a = "java";
String b = new String("java");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
new String cria um objeto separado, enquanto String.equals compara seus caracteres. A comparação de texto distingue maiúsculas e minúsculas: "Java".equals("java") é falso. equalsIgnoreCase faz uma comparação sem essa distinção quando essa é a regra desejada.
Por que a identidade às vezes parece comparar conteúdo
Literais de string iguais compartilham um objeto de cadeia de caracteres, então "java" == "java" é verdadeiro. Isso pode ocultar um erro em um teste pequeno. Um texto obtido de uma entrada ou construído durante a execução de um programa não precisa compartilhar esse objeto. Use equals para comparar conteúdo, independentemente de como o texto foi obtido.
Comparações de objetos empacotadores têm uma armadilha semelhante:
Integer a = 127, b = 127;
Integer c = 1000, d = 1000;
System.out.println(a == b); // true
System.out.println(c.equals(d)); // true
O empacotamento automático reutiliza objetos Integer para valores de -128 a 127. O fato de valores maiores compartilharem ou não um objeto não é uma regra portátil de comparação numérica. Em uma configuração padrão, os dois objetos empacotadores de 1000 são separados, mas um cache maior é permitido. Use o equals do tipo empacotador para comparar seus valores ou compare valores primitivos. Se um lado de == for um int, Java desempacota o objeto empacotador e compara valores numéricos; desempacotar um objeto empacotador nulo falha.
Decida quais campos definem a igualdade
Para um catálogo de livros, título e ano de publicação podem definir a igualdade, enquanto quem está com o livro emprestado é ignorado. Esses campos selecionados devem orientar o método de igualdade. Outro programa poderia escolher um identificador único; a regra útil depende do que seus objetos representam.
Um ArrayList usa equals em contains, indexOf e remove(Object). Suponha que Book apenas armazene campos e herde equals. Adicionar um new Book("Dune", 1965) não torna contains(new Book("Dune", 1965)) verdadeiro: a segunda expressão cria um objeto diferente. Sobrescrever a igualdade por título e ano permite que uma busca encontre esse conteúdo igual.
HashSet e HashMap também usam hashCode para reduzir o conjunto de objetos candidatos antes de verificar a igualdade. Portanto, definir igualdade de conteúdo também exige uma regra correspondente de código de dispersão.
Principais conclusões
- Para referências,
==testa identidade, enquantoequalssegue a regra da classe. Object.equalscompara identidade até ser sobrescrito.- Use
String.equalspara comparar conteúdo textual. - Literais compartilhados e o cache de objetos empacotadores podem tornar testes de identidade enganosos.
- Escolha deliberadamente os campos de igualdade; as coleções dependem dessa escolha.
4.2 Implementação correta de equals
Um método de igualdade deve substituir o equals(Object) herdado e comparar os campos que definem o significado do objeto. A assinatura correta é tão importante quanto as comparações dentro do método.
Um padrão completo de igualdade
Esta classe usa título e ano, permite um título nulo e mantém fixos os campos de igualdade:
import java.util.Objects;
final class Book {
private final String title;
private final int year;
Book(String title, int year) {
this.title = title;
this.year = year;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Book)) {
return false;
}
Book book = (Book) other;
return year == book.year
&& Objects.equals(title, book.title);
}
@Override
public int hashCode() {
return Objects.hash(title, year);
}
}
Leia equals em quatro etapas. A verificação de identidade aceita imediatamente o mesmo objeto. A verificação de tipo rejeita tudo que não é um livro, incluindo null. A conversão explícita fornece uma variável Book pela qual os campos podem ser lidos. A expressão final verifica os dois campos de igualdade.
O tipo do parâmetro é Object, não Book. Escrever equals(Book other) faz uma sobrecarga do nome com outro método e deixa o equals(Object) herdado inalterado. Uma chamada direta pode então parecer funcionar enquanto ArrayList.contains ainda usa identidade. @Override faz o compilador detectar esse erro de assinatura.
A partir do Java 16, a verificação de tipo e a conversão explícita podem ser combinadas: if (!(other instanceof Book book)) { return false; }. Após a verificação ser bem-sucedida, book fica disponível para comparar os campos.
Adeque as comparações aos tipos dos campos
Use == para campos de tipos inteiros, de caracteres e booleanos. Use o equals de um objeto para seu conteúdo. Para um campo de objeto que pode ser nulo, Objects.equals(a, b) retorna verdadeiro quando ambos são nulos, falso quando apenas um é nulo e, caso contrário, usa igualdade.
Para campos double, Double.compare(x, other.x) == 0 fornece tratamento consistente de valores especiais: dois valores NaN são comparados como iguais, e zero positivo e negativo são distinguidos. Combine essa escolha com uma regra de hash compatível. O == direto de ponto flutuante trata NaN de forma diferente.
Verifique o contrato de igualdade
Para referências não nulas, a igualdade deve ser:
- Reflexiva:
x.equals(x)é verdadeiro. - Simétrica:
x.equals(y)ey.equals(x)concordam. - Transitiva: se
xé igual ayeyé igual az,xé igual az. - Consistente: chamadas repetidas concordam enquanto os dados comparados permanecem inalterados.
- Falsa para null:
x.equals(null)é falso.
Dois livros para “Dune”, 1965, são comparados como iguais; alterar um dos anos para 1984 os torna desiguais. Um livro não deve ser comparado como igual a uma cadeia de caracteres que contém seu título: a cadeia rejeitaria o livro, violando a simetria.
Algumas implementações usam other == null || getClass() != other.getClass() para verificar o tipo. Isso rejeita classes diferentes em tempo de execução. instanceof pode aceitar subclasses; para a classe final acima, não existe subclasse e as duas verificações concordam.
Principais conclusões
- Sobrescreva com
public boolean equals(Object other)e@Override. - Verifique a identidade e o tipo, converta a referência e compare os campos de igualdade.
- Use comparações de objetos seguras para valores nulos quando campos puderem estar ausentes.
- Respeite a reflexividade, a simetria, a transitividade, a consistência e a rejeição de null.
equals(Book)sobrecarrega o método e não substituiequals(Object).- As escolhas de verificação de tipo importam quando subclasses são permitidas.
4.3 Códigos de dispersão correspondentes e chaves estáveis
Um código de dispersão é um inteiro usado para restringir uma busca. HashSet e HashMap o usam para encontrar objetos candidatos e depois usam equals para confirmar uma correspondência. Ele não é um identificador único de objeto.
O contrato vale em uma direção
Objetos iguais devem retornar o mesmo código de dispersão. Objetos desiguais também podem compartilhar um código; isso é uma colisão. Uma colisão não os torna iguais, pois a igualdade ainda é verificada.
Um código de dispersão deve permanecer consistente enquanto as informações usadas por equals não mudarem. Em uma implementação correta, códigos diferentes excluem a igualdade, mas códigos iguais não a estabelecem.
Quando uma classe sobrescreve equals, mas herda Object.hashCode, objetos separados e iguais geralmente recebem códigos diferentes. Um conjunto pode então manter ambos, e uma consulta em um mapa usando uma nova chave igual pode não encontrar a entrada. A falha exata não é garantida, pois objetos diferentes podem ter colisões nos códigos herdados. Corrija o contrato em vez de depender de uma falha específica de consulta.
Calcule a partir dos campos de igualdade
Para a classe de livro que compara título e ano:
@Override
public int hashCode() {
return Objects.hash(title, year);
}
Importe java.util.Objects. Os mesmos valores de campo agora produzem o mesmo resultado. Um conjunto de livros armazena uma entrada após a adição de dois livros iguais, e um mapa pode recuperar um valor usando uma nova chave de livro igual.
Objects.hash combina os hashes dos campos usando 31. Para “Dune”, o hash da cadeia de caracteres é 2141736. Combinar esse título e o ano 1965 resulta em 31 multiplicado pela soma de 31 e 2141736, mais 1965: o resultado é 66396742.
O cálculo de hash de strings também usa 31. Para “ab”, os códigos de caractere 97 e 98 resultam em 97 multiplicado por 31 mais 98, ou 3105. Cálculos mais longos podem ultrapassar o intervalo de int e dar a volta, então um código de dispersão negativo é normal.
| Regra de hash | Consequência para o contrato |
|---|---|
| Usar os mesmos campos de igualdade | Objetos iguais recebem códigos correspondentes |
| Adicionar um campo que a igualdade ignora | Objetos iguais podem receber códigos diferentes |
| Usar menos campos de igualdade | Permitido, mas pode criar mais colisões |
| Sempre retornar 1 | Permitido, mas prejudica o desempenho da busca |
Não altere a chave durante o armazenamento
Suponha que o ano de um livro seja mutável e afete seu código de dispersão. Após a inserção em um conjunto, alterar o ano pode fazer contains(book) falhar mesmo com a mesma referência: a consulta agora busca usando um código diferente. O conjunto ainda pode informar tamanho 1.
Mantenha estáveis os campos de igualdade enquanto um objeto for elemento de um conjunto ou chave de um mapa. Se eles precisarem mudar, remova o objeto enquanto seus valores antigos ainda estiverem em vigor, altere-o e insira-o novamente. Tentar removê-lo somente após a alteração pode causar o mesmo problema de consulta.
A igualdade gerada ainda precisa de uma escolha adequada de dados
Java 16 e versões posteriores oferecem records, como record Book(String title, int year) {}. Um record gera os métodos de acesso title() e year(), além de igualdade, hash e uma representação textual legível a partir de todos os seus componentes. Os campos dos componentes são final. Esse exemplo é um valor imutável útil porque seus componentes são uma cadeia de caracteres e um inteiro; um record que armazenasse uma lista mutável ainda compartilharia o estado dessa lista.
Para classes comuns, uma IDE pode gerar equals e hashCode juntos. Selecione os campos que definem a igualdade desejada e mantenha os dois métodos alinhados quando essa escolha mudar.
Principais conclusões
- Objetos iguais exigem códigos de dispersão iguais; colisões entre objetos desiguais são permitidas.
- Sobrescreva
hashCodesempre que sobrescreverequals. - Calcule o hash apenas de dados compatíveis com a regra de igualdade.
- Mantenha estáveis os campos relevantes para o hash enquanto o objeto pertencer a um conjunto ou for usado como chave de mapa.
- Remova uma chave antes de alterá-la e depois insira-a novamente.
- Records geram igualdade a partir de seus componentes; componentes mutáveis ainda exigem cuidado.
5Atribua uma tarefa clara a cada classe
Um programa textual tem dois tipos de tarefa: conversar com o usuário e gerenciar os dados e as regras. Manter essas tarefas em classes separadas facilita a verificação e a reutilização das regras. Conecte as classes por meio de parâmetros de construtor e pequenos métodos públicos. Depois, adicione um recurso primeiro onde sua regra pertence, verifique o resultado com valores conhecidos e forneça à interface um comando que o chame e exiba a resposta.
5.1 Entrada, saída e regras do programa
A interface do usuário lê entradas e exibe saídas. A lógica do programa armazena dados e aplica regras. Um projeto separado passa valores para a lógica e recebe resultados; suas classes de lógica não usam Scanner nem System.out.
Conecte os objetos em main
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
TemperatureLog log = new TemperatureLog();
UserInterface ui = new UserInterface(scanner, log);
ui.start();
}
Com import java.util.Scanner;, essa montagem cria um leitor de entrada, um registro e uma interface. Ela pertence à classe de entrada do programa. UserInterface armazena os objetos fornecidos em campos para que todos os comandos trabalhem com o mesmo registro.
| Tarefa | Classe responsável |
|---|---|
| Ler texto de comando | Interface do usuário |
| Armazenar uma leitura | Registro de temperaturas |
| Calcular média ou máximo | Registro de temperaturas |
| Arredondar e exibir um resultado | Interface do usuário |
| Rejeitar uma temperatura impossível | Registro de temperaturas |
| Explicar uma entrada inválida ao usuário | Interface do usuário |
Retorne valores que qualquer chamador possa usar
Um registro pode fornecer add(double celsius), count(), average() e max(). Leituras de 18,5, 21,0 e 19,5 °C têm soma 59,0, contagem 3, máximo 21,0 e média de aproximadamente 19,6667 °C. A interface pode arredondar a média retornada com Math.round(average * 10) / 10.0 e exibir 19,7 °C.
Retornar o número facilita verificações diretas. Um método chamado printAverage() que apenas exibe o resultado acopla um cálculo à saída no console, o que dificulta a reutilização.
Para um laço simples de comandos, suponha que add aceite temperaturas válidas e count retorne sua quantidade:
class UserInterface {
private Scanner scanner;
private TemperatureLog log;
UserInterface(Scanner scanner, TemperatureLog log) {
this.scanner = scanner;
this.log = log;
}
void start() {
while (true) {
String command = scanner.nextLine();
if ("quit".equals(command)) {
break;
}
if ("count".equals(command)) {
System.out.println(log.count());
}
}
}
}
O laço cuida da interação. Ele solicita um resultado ao registro e o exibe sem saber como as leituras são armazenadas. Um comando de média deve tratar um registro vazio de acordo com o resultado documentado da lógica ou verificar count() antes de chamar um método que exige leituras.
Valide no ponto adequado
A lógica pode fazer add retornar falso sem armazenar uma leitura abaixo de -273,15 °C. Assim, todos os chamadores recebem a mesma regra de dados. A interface decide como informar essa rejeição e solicitar a entrada novamente. Converter uma entrada não numérica em uma mensagem de erro útil também é tarefa da interface.
Use um Scanner para System.in e passe-o aos leitores. Vários scanners podem disputar entradas armazenadas em buffer, e fechar um deles fecha o fluxo de entrada subjacente. Mantenha o tempo de vida do scanner compartilhado sob controle do código responsável pela entrada do programa.
A lógica pode mais tarde atender a outra interface por meio dos mesmos métodos públicos. A reutilização vem de manter cálculos e regras de validade disponíveis como resultados de métodos, independentemente de um laço de comandos específico.
Principais conclusões
- A interface lê e exibe; a lógica armazena dados, aplica regras e retorna resultados.
mainconstrói os objetos compartilhados e inicia a interface.- Passe um scanner e o objeto de lógica ao construtor da interface.
- Regras de dados pertencem à lógica; mensagens de entrada e formatação de exibição pertencem à interface.
- Retornar resultados facilita verificações diretas e a reutilização da interface.
5.2 Adicione um recurso e verifique seu comportamento
Um programa separado pode crescer com uma pequena alteração por vez. Dê à lógica um método útil, verifique-o diretamente e depois conecte um comando que reúna suas entradas e exiba seu resultado.
Adicione uma consulta à lógica
Um TemperatureLog que armazena um ArrayList<Double> readings inicializado pode contar leituras acima de um limite:
int countAbove(double limit) {
int count = 0;
for (double reading : readings) {
if (reading > limit) {
count++;
}
}
return count;
}
A comparação estrita exclui leituras exatamente iguais ao limite. Para 18,5, 21,0, 19,5 e 23,0 °C, countAbove(20.0) retorna 2. Coloque a comparação no registro para que todos os chamadores recebam a mesma regra.
O comando da interface lê um limite, chama log.countAbove(limit) e exibe a contagem. Se cada comando ficar longo, divida start() em métodos auxiliares privados como addReading() e showStatistics(). Os métodos auxiliares organizam a interação; eles não retiram as regras de dados do registro.
Verifique sem digitar comandos
Crie o objeto de lógica e chame seus métodos com valores cujas respostas você conhece:
TemperatureLog log = new TemperatureLog();
log.add(18.5);
log.add(21.0);
log.add(19.5);
log.add(23.0);
System.out.println(log.count() == 4);
System.out.println(log.average() == 20.5);
System.out.println(log.max() == 23.0);
System.out.println(log.countAbove(20.0) == 2);
A soma é 82,0, então a média esperada é 20,5. Esses valores específicos têm representações binárias exatas, tornando essas verificações diretas de igualdade adequadas para o exemplo. Verifique também um registro vazio de acordo com seu comportamento documentado, um limite igual a uma leitura e a rejeição de uma entrada inválida sem alterar a contagem. Ferramentas de teste automatizado se baseiam na mesma ideia de uma entrada conhecida e um resultado esperado.
Altere o armazenamento apenas quando ele ainda atender às consultas
Uma interface que chama apenas average, count e max não precisa saber se o registro armazena uma lista. Campos de soma acumulada, contagem e máximo podem responder a essas três consultas sem manter todas as leituras.
Uma consulta arbitrária countAbove(limit) precisa de mais informações. As amostras 18, 22, 23 e 20, 20, 23 têm a mesma soma 63, contagem 3 e máximo 23, mas suas contagens acima de 20 são 2 e 1. Manter apenas essas três medidas agregadas perde a distinção. Os métodos públicos descrevem o que deve continuar funcionando após uma alteração de armazenamento.
Combine as ideias de projeto
Um livro de receitas pode ser proprietário de uma lista privada de receitas. Cada receita pode conter ingredientes medidos em gramas e um tempo de preparo em minutos. A igualdade pode ajudar nas verificações de duplicidade, enquanto uma interface adiciona, lista e busca receitas por meio dos métodos do livro. Uma lista de resultados copiada pode proteger sua composição e ainda compartilhar objetos de receita; decida se os chamadores podem alterar essas receitas.
Trabalhe em pequenas etapas: implemente um método, verifique-o e depois adicione seu comando. Se uma alteração prejudicar um resultado existente, volte a um estado funcional e isole essa alteração antes de adicionar outro recurso.
Principais conclusões
- Adicione e verifique o método de lógica antes de conectar seu comando na interface.
- Use entradas conhecidas e resultados esperados para verificar a lógica diretamente.
- Inclua casos de valor-limite, como uma coleção vazia ou um limiar exato.
- Uma alteração de armazenamento deve preservar informações suficientes para cada consulta pública.
Exercícios resolvidos de Objetos e referências em Java
1Acompanhe variáveis sem perder os objetos
15 passos
- 1IntroduçãoDesenhe variáveis e objetos
Uma variável pode armazenar um número diretamente ou uma referência que aponta para um objeto. Essa diferença explica por que copiar uma variável às vezes fornece dados independentes e às vezes compartilha uma alteração.
Vamos desenhar variáveis separadamente dos objetos e depois acompanhar cada instrução. Mantenha o número de setas separado do número de objetos: várias setas podem terminar em um único objeto.
- 2ExplicaçãoUm valor pode ser um dado ou uma referência
Java tem oito tipos primitivos:
byte,short,int,long,float,double,chareboolean. Suas variáveis armazenam dados diretamente. Classes e vetores são tipos de referência; suas variáveis armazenam referências ounull.Em
int count = 13;, o valor é 13. EmTally t = new Tally(13);, o valor emté uma referência a um objeto que armazena seus próprios dados. - 3ExemploCopie um valor primitivo
Acompanhe as atribuições em ordem:
int start = 13; int next = start; next += 2;A segunda instrução copia 13 para
next. A terceira lê essa cópia e a substitui por 15.startpermanece 13 porque nenhuma instrução lhe atribui um novo valor. Desenhe duas caixas contendo números separados; não há objeto compartilhado envolvido. - 4ExemploCopie um valor de referência
Suponha que
Tallyarmazene um inteiro,addOne()o incremente evalue()o leia.Tally first = new Tally(13); Tally next = first; next.addOne();Um
newcria um contador. A atribuição fornece anextoutro caminho até ele. ChamaraddOne()altera seu valor armazenado para 14, quefirst.value()agora também lê. A segunda variável não é um segundo contador. - 5DesafioSepare um objeto de seu apelido
Com os mesmos métodos de
Tally, o quea.value()retorna?Tally a = new Tally(20); Tally b = new Tally(a.value()); b.addOne();---reveal---
Ele retorna 20. O segundo
newcria um contador independente inicializado com 20; seu incremento altera apenas esse segundo objeto para 21. Ler um valor de campo e usá-lo em um construtor é diferente de copiar a referência ao objeto original. - 6ConexãoAlguns objetos compartilhados não podem ser alterados
Strings e tipos empacotadores como
Integersão tipos de referência. O empacotamento automático converte valores primitivos em objetos empacotadores para usos comoArrayList<Integer>.Strings são imutáveis: se
lefterightcompartilham “cedar”, atribuirright = right.toUpperCase()não altera os caracteres originais.leftcontinua sendo “cedar”. O resultado em maiúsculas retornado é atribuído apenas aright. - 7AnalogiaUse setas para acompanhar alterações
Pense em cada variável como um cartão que mostra um caminho até um objeto. Copiar o cartão fornece outro caminho para o mesmo destino. Escrever um caminho diferente em um cartão não reescreve os outros.
Em um diagrama de memória, desenhe uma seta de cada variável de referência até seu objeto. Um método modificador altera os valores dos campos do objeto; uma atribuição altera a seta de uma variável. Mantenha essas duas alterações visualmente distintas.
- 8ExemploAltere primeiro o estado compartilhado
Considere que
Cratearmazene uma massa em quilogramas, com métodossetMassemass.Crate a = new Crate(3); Crate b = a; b.setMass(6);Desenhe
aebapontando para uma caixa. O método modificador atualiza a massa dentro dessa caixa de 3 para 6. Tantoa.mass()quantob.mass()agora retornam 6; o método modificador não redirecionou nenhuma das variáveis. - 9ExemploDepois, redirecione uma variável
Continue o rastreamento da caixa:
b = new Crate(9);Crie um segundo objeto com massa 9 e redirecione apenas
b. A variávelaainda aponta para a caixa original, cuja massa permanece 6. Agora há dois objetos de caixa alcançáveis. Isso é uma reatribuição, então nada na massa armazenada da primeira caixa é reiniciado. - 10DesafioMantenha o caminho restante
Continue com
Crate c = a; a = null;. Por qual variável a caixa de 6 kg ainda pode ser alcançada e ela está disponível para coleta?---reveal---
Ela ainda está alcançável por meio de
c, então não está disponível para coleta. A atribuição anterior copiou a referência parac; atribuir null aaremoveu apenas um caminho. A caixa separada de 9 kg permanece alcançável por meio deb. - 11Ideia-chaveUm campo ou uma lista pode preservar um caminho
Quando o último caminho utilizável até um objeto desaparece, ele se torna inalcançável e fica disponível para coleta de lixo. Java escolhe o momento da coleta; perder uma referência não garante recuperação imediata da memória.
Um caminho pode passar por outro objeto ou lista. Antes de considerar um objeto inalcançável, verifique essas conexões e também as variáveis locais. Uma entrada de lista pode preservar um objeto depois que sua variável original sair de escopo.
- 12ExplicaçãoNull significa que o destino está ausente
nullnão é um objeto vazio. UmaStringnula não tem um objeto no qual chamarlength(), enquanto""é uma cadeia de caracteres utilizável de comprimento zero. Campos de referência têm null como padrão; variáveis locais precisam receber um valor antes de serem lidas.Tipos empacotadores também podem ser null.
Integer n = null; int count = n;lançaNullPointerExceptionquando o desempacotamento tenta obter um valor primitivo do objeto empacotador ausente. - 13DicaLeia o erro na chamada que falhou
Para um erro de null, localize o número da linha e depois a chamada de instância, o acesso a campo ou o desempacotamento nessa linha. Mensagens explicativas, ativadas por padrão a partir do Java 15, podem identificar o campo ausente.
Acompanhe o código para trás até o construtor, o resultado de busca ou a atribuição que deveria ter fornecido um objeto. Ver “null” no texto exibido é uma pista: exibir uma cadeia nula pode funcionar mesmo que uma chamada de instância posterior falhe.
- 14DesafioProteja o rastreamento combinado
Uma busca atribui
Crate found = findCrate();, retornando uma caixa ou null. Qual condição verifica com segurança se a massa é maior que 5 kg? Por que seus testes devem estar nessa ordem?---reveal---
Use
found != null && found.mass() > 5. O teste de null é executado primeiro, e&&ignoramass()quando não existe uma caixa. Colocar a chamada de método primeiro desreferenciaria null antes de chegar à verificação. Uma referência retornada segue as mesmas regras dos apelidos de referência acompanhados anteriormente. - 15RecapitulaçãoVerifique um rastreamento de referências
Para um novo rastreamento, marque cada
new, copie o valor em cada atribuição e atualize apenas o campo afetado por uma mutação. Depois, acompanhe todos os caminhos restantes antes de decidir que um objeto está inalcançável.Em uma linha que falha, identifique a operação exata que precisa de um objeto. Verifique null antes dessa operação ou inicialize o campo com o objeto de que o programa realmente precisa, como uma lista vazia.
2Acompanhe um objeto na entrada e na saída de um método
12 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhe uma chamada e seu resultado
- 2ExplicaçãoUm parâmetro recebe seu próprio valor
- 3ExemploAltere o objeto compartilhado
- 4ExemploReatribua o caminho local
- 5DesafioUm método pode trocar as variáveis de quem o chamou?
- 6ConexãoCompare o objeto receptor com seu argumento
- 7ExplicaçãoO tipo de retorno não promete uma cópia
- 8ExemploConstrua um controle independente para retornar
- 9DesafioEncontre uma entrada que possa ser atualizada
- 10DicaUm método de acesso pode revelar um apelido oculto
- 11DesafioCombine um retorno com uma chamada que recebe um parâmetro
- 12RecapitulaçãoExamine as duas fronteiras de um método
3Construa e proteja uma coleção de objetos
16 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoSepare as camadas de estado
- 2ExplicaçãoModele uma relação de posse
- 3ExemploDelegue a regra de datas
- 4DesafioProteja a data recebida
- 5ExplicaçãoUma entrada é outro caminho
- 6ExemploCalcule um total por meio de um laço
- 7ExemploMantenha o objeto selecionado
- 8DesafioRetorne a ausência no momento certo
- 9DicaSepare a remoção do percurso
- 10ExplicaçãoDê ao proprietário uma lista inicializada
- 11ExemploOfereça operações úteis
- 12DesafioDistinga uma cópia da lista de seus elementos
- 13ConexãoProteja as duas direções
- 14ExemploFiltre para um novo resultado
- 15DesafioAcompanhe uma cópia por meio de outro objeto
- 16RecapitulaçãoRevise as fronteiras da coleção
4Construa uma igualdade confiável para coleções
16 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoDecida o que significa uma correspondência
- 2ExemploTexto igual em objetos separados
- 3DicaNão deixe o cache escolher a regra
- 4DesafioDê igualdade de conteúdo a uma classe
- 5ExplicaçãoEscolha os campos e a assinatura
- 6ExemploAceite a identidade e rejeite tipos incorretos
- 7ExemploCompare os campos selecionados
- 8DesafioDetecte uma violação do contrato
- 9Ideia-chaveVerifique mais de um par correspondente
- 10ExplicaçãoCódigos de dispersão restringem os candidatos
- 11ExemploGere o hash correspondente
- 12DesafioDistinga colisão de igualdade
- 13ExemploObserve o efeito de uma chave alterada
- 14ConexãoUm record simples fornece os dois métodos
- 15DesafioAcompanhe o conjunto após a igualdade gerada
- 16RecapitulaçãoRevise uma classe de valor
5Adicione um recurso sem misturar entrada e regras
11 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoConecte regras a uma conversa
- 2ExplicaçãoNomeie as duas tarefas
- 3ExemploConecte um conjunto de objetos compartilhados
- 4ExemploRetorne o resultado para exibição
- 5DesafioColoque uma rejeição na classe adequada
- 6ExplicaçãoDê à nova consulta um método de lógica
- 7ExemploVerifique com entradas conhecidas
- 8DesafioAvalie a proposta de armazenamento
- 9DicaAmplie o programa uma etapa verificada por vez
- 10DesafioPasse para outra interface
- 11RecapitulaçãoRevise um novo recurso em sua fronteira
Questões de Objetos e referências em Java
Conjunto 1: Acompanhe valores, chamadas e comparações simples10 Questões de Prática
Questão 1 de 10
label pode ser null. Qual condição verifica com segurança se ele tem pelo menos três caracteres?Mostrar respostaOcultar resposta
&& ignora a chamada de length quando essa verificação é falsa. Colocar a chamada de length primeiro já desreferencia o objeto ausente. Um único & também avalia os dois lados, portanto não fornece a proteção de curto-circuito necessária.Questão 2 de 10
O que original armazena após este código?
int original = 8;
int duplicate = original;
duplicate = 11;
Mostrar respostaOcultar resposta
duplicate, então original ainda armazena 8. Tratar as variáveis como apelidos de referência resultaria em 11, mas uma atribuição de int copia o próprio número e não cria um objeto compartilhado.Questão 3 de 10
Lamp tem um campo booleano inicialmente falso; turnOn() define-o como verdadeiro e isOn() o lê. O que a.isOn() e b.isOn() retornam, nessa ordem?
Lamp a = new Lamp();
Lamp b = a;
b.turnOn();
b = new Lamp();
Mostrar respostaOcultar resposta
a e b. A reatribuição de b faz com que ele aponte para uma nova lâmpada, cujo campo começa como falso, enquanto a mantém a lâmpada acesa. Prever dois resultados verdadeiros deixa de considerar o novo objeto criado pela instrução final.Questão 4 de 10
Robot r não nulo. Ele executa apenas r = new Robot();. O que acontece com a variável de quem chamou o método que foi passada como argumento?Mostrar respostaOcultar resposta
Questão 5 de 10
Gauge.copy() retorna new Gauge(value). Um medidor começa em 6, e raise() soma 1. Quais valores first e second informam?
Gauge first = new Gauge(6);
Gauge second = first.copy();
second.raise();
Mostrar respostaOcultar resposta
Questão 6 de 10
Device armazena diretamente seu argumento Settings mutável. Seu método de acesso retorna uma cópia das configurações armazenadas. Dois dispositivos recebem o mesmo objeto de configurações, e o chamador altera esse objeto original depois. Qual alteração de projeto torna cada dispositivo independente dessa alteração externa e do outro dispositivo?Mostrar respostaOcultar resposta
Questão 7 de 10
Sensor s não nulo é adicionado a duas listas. Uma lista remove sua entrada de s. O que acontece com o sensor na outra lista?Mostrar respostaOcultar resposta
Questão 8 de 10
O que estas duas expressões retornam?
String word = new String("maple");
word == "maple"
word.equals("maple")
Mostrar respostaOcultar resposta
String.equals compara os caracteres e retorna verdadeiro. Textos iguais não tornam idênticas strings criadas separadamente, embora literais iguais possam compartilhar um objeto.Questão 9 de 10
Object?Mostrar respostaOcultar resposta
equals, o tipo de retorno boolean e o tipo de parâmetro Object. Corresponder a essa declaração o sobrescreve; adicionar @Override permite que o compilador verifique a correspondência. Um parâmetro do tipo Note cria uma sobrecarga e mantém o método herdado.Questão 10 de 10
Mostrar respostaOcultar resposta
Conjunto 2: Aplique compartilhamento, coleções e fronteiras do programa10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Meter armazena um inteiro e tem set(int) e get(). O que m.get() retorna após a chamada?
static void adjust(Meter x) {
x.set(14);
x = new Meter(22);
x.set(25);
}
Meter m = new Meter(9);
adjust(m);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
O que acontece com a após a última linha?
String a = "river";
String b = a;
b = b.toUpperCase();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
O que acontece quando este código é executado?
Integer reading = null;
int value = reading;
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Bin.items() retorna diretamente sua lista privada de objetos Part mutáveis. Um chamador deve poder inspecionar e modificar a lista e as peças retornadas sem alterar a composição do recipiente nem o estado das peças armazenadas. O que o resultado deve conter?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
return null em um else após o teste de nome dentro do laço. Onde esse retorno deve ficar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
copy é criada com new ArrayList<>(original). Ambas as listas contêm inicialmente o mesmo Clip c mutável. O chamador altera c por meio de copy.get(0) e depois esvazia copy. O que permanece em original?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Badge tem campos e um construtor, mas não sobrescreve equals. Qual é o resultado?
new Badge("Q7").equals(new Badge("Q7"))
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Tag.equals compara apenas code. Tag.hashCode usa code e o campo mutável color. Duas etiquetas têm o mesmo código, mas cores diferentes. Uma pessoa desenvolvedora propõe retornar um código de dispersão constante para corrigir o comportamento. Como as duas regras devem ser avaliadas?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
count(), average() e max(). A pessoa desenvolvedora adiciona countAbove(limit) para qualquer limite fornecido pelo chamador e depois substitui todas as leituras apenas por soma, contagem e máximo. Por que a nova consulta não pode mais ser implementada corretamente para todos os registros?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Depure apelidos de referência, igualdade e projeto de coleções10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
countAbove(limit) conta leituras estritamente maiores que seu argumento. Um registro contém 11,5, 15,0, 15,0 e 18,5 °C. O que uma verificação direta deve esperar de countAbove(15.0)?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Existem apenas as referências mostradas, e todas as variáveis continuam em uso. Cell tem dados de valor mutáveis. Quantos objetos de célula permanecem alcançáveis?
Cell a = new Cell(3);
Cell b = new Cell(8);
Cell c = a;
a = b;
b = null;
c = a;
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
private ArrayList<Note> notes;. Seu construtor nunca atribui um valor a notes, e add chama imediatamente notes.add(note). Qual alteração deixa uma instância recém-construída pronta para armazenar notas?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Box tem um campo inteiro lido por get() e alterado por set(int). Quais valores left.get() e right.get() informam após esta chamada?
static void revise(Box x, Box y) {
x.set(10);
x = y;
x.set(16);
}
Box left = new Box(4);
Box right = new Box(7);
revise(left, right);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Member armazena um SimpleDate joined não nulo e precisa decidir se entrou antes de outro membro. Onde a comparação de ano, mês e dia deve ser implementada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Queue armazena diretamente um ArrayList<Job> recebido, mas seu método de acesso retorna uma nova lista superficial. O chamador esvazia a lista de entrada original. A fila deve manter sua composição original, enquanto os objetos de tarefa podem continuar compartilhados. Qual correção atende a esse requisito?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Token declara public boolean equals(Token other) sem @Override. Comparações diretas parecem corretas, mas um ArrayList<Token> não encontra um novo token com campos correspondentes. Qual é o problema de assinatura?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Key.equals e hashCode usam serial. Uma chave mutável é adicionada a um HashSet, depois seu número de série muda e uma consulta falha. Qual sequência permite corretamente uma alteração planejada do número de série preservando a presença da chave no conjunto?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Mark.equals compara code. Uma lista armazena marcas com códigos “A”, “B”, “A”; as duas marcas A são objetos separados. Uma segunda lista contém apenas uma referência à primeira marca A. O que permanece após marks.removeAll(second)?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Analise cópias, contratos e alterações de projeto10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
record Stamp(String code, int batch) {} usa igualdade e hash gerados. Um conjunto recebe new Stamp("R", 4), um new Stamp("R", 4) separado e depois new Stamp("R", 5). Quantas entradas ele contém?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Node contém dados mutáveis. Existem apenas as referências mostradas. Na última linha, quantos objetos de nó estão alcançáveis por meio de first ou da lista?
Node first = new Node();
ArrayList<Node> nodes = new ArrayList<>();
nodes.add(first);
first = new Node();
nodes.add(first);
nodes.remove(0);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Locker.find(id) retorna atualmente um Item mutável recém-construído com os mesmos valores de campo do item armazenado correspondente. Os chamadores usam o resultado para atualizar o rótulo do item armazenado. Qual comportamento o método deve oferecer para que essas atualizações afetem o armário?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Schedule armazenam um campo SimpleDate mutável e compartilhado. Uma pessoa desenvolvedora torna apenas os campos das agendas final. O que acontece se o objeto de data original for alterado por meio de seu método modificador?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Document mutáveis. matching(prefix) constrói uma nova lista contendo documentos existentes correspondentes. Um chamador remove um resultado e edita o título de outro. Que efeito isso pode ter na biblioteca?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
x e y, o que x == y estabelece quando é verdadeiro?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Score.equals(Object) aceita outra pontuação quando a diferença absoluta entre seus valores inteiros é no máximo 2. As pontuações x, y e z armazenam 10, 12 e 14. Nenhum valor muda. Qual problema do contrato de igualdade aparece nesses três objetos?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Double.compare(level, other.level) == 0 em equals. Um método de hash proposto sempre retorna 1. Outro retorna códigos diferentes para objetos distintos cujos níveis são ambos NaN. Como essas propostas se comparam ao contrato de igualdade?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Alcançabilidade
Apelido de referência
Box second = first;, second é um apelido de referência e seu método modificador altera a caixa alcançada por first.Argumento
gate.allowIn(ana), o argumento é o valor de referência lido de ana, que o parâmetro copia.Avaliação de curto-circuito
&& quando seu lado esquerdo é falso, permitindo que uma verificação anterior proteja uma chamada posterior.name != null && name.length() > 0 ignorar a chamada de método quando o valor é null.C
Cache de objetos empacotadores
equals em vez de depender de == entre objetos empacotadores.Campo de referência final
add.Campo de tipo de referência
birthday em Person pode apontar para um SimpleDate que realiza comparações de datas.Campos que definem a igualdade
Chave de HashMap
Classe ArrayList
ArrayList<Parcel> pode armazenar três referências a pacotes e permitir que um laço some seus pesos em quilogramas.Classe empacotadora
Integer fornece o tipo de elemento para ArrayList<Integer>; o tipo primitivo int não pode fazê-lo.Classe HashSet
HashSet<Book> mantém uma entrada para dois livros iguais e separados quando sua igualdade e seus hashes concordam.Código de dispersão
Objects.hash(title, year) quando esses campos definem a igualdade.Coleta de lixo
Colisão de hash
equals os distingue corretamente.Composição
Composição da lista
Consistência
Consulta à coleção
songsLongerThan(200) constrói uma nova lista mantendo as referências às músicas correspondentes.Contrato público
Cópia defensiva
Cópia superficial
D
Delegação
olderThan chama birthday.before(other.birthday) para comparar datas.Desempacotamento
NullPointerException para um objeto empacotador nulo.int count = boxed;, então boxed deve se referir a um objeto Integer real.E
Empacotamento automático
numbers.add(12) passe um valor Integer quando numbers é um ArrayList<Integer>.Encapsulamento
Exceção ConcurrentModificationException
ConcurrentModificationException antes do término do percurso.Exceção NullPointerException
name.length() lança NullPointerException quando a variável name armazena null.I
Identidade
== para duas referências; valores de campo correspondentes, por si só, não a estabelecem.Igualdade de conteúdo
Imutabilidade
toUpperCase() não pode alterar os caracteres da cadeia original.Injeção pelo construtor
Interface do usuário
add de um registro de temperaturas e exibe qualquer mensagem de rejeição.Internalização de strings
"oak" == "oak" ser verdadeiro, ocultando um erro que aparece quando um texto igual vem da entrada.L
Laço for-each
getWeight() sem alterar a composição da lista.Lógica do programa
M
Medida agregada
Método de cópia
new Counter(value), então incrementar a cópia deixa o original inalterado.Método fábrica
issue() de uma máquina de tíquetes constrói um tíquete com seu próximo número e o retorna.Mutação
parcel.setWeight(3.5) é uma mutação que uma lista contendo esse pacote também pode observar.O
Objeto mutável
Objeto proprietário
add.Objeto receptor
this se refere a ele.earlier.before(later), o objeto receptor é earlier; a outra data é fornecida como argumento.P
Parâmetro
allowIn(Person visitor), o parâmetro visitor recebe a referência à pessoa fornecida pelo chamador.Passagem por valor
R
Reatribuição
current = new Counter(); redireciona current, enquanto outros apelidos mantêm o contador antigo.Referência
Counter second = first; permite que ambas as variáveis alcancem o contador original.Referência nula
null, que não aponta para nenhum objeto e pode ser armazenado por uma variável de referência, mas não por uma variável de tipo primitivo.String name não pode receber length(), enquanto uma cadeia vazia pode retornar comprimento 0.Reflexividade
x.equals(x).this == other em equals garante a reflexividade antes de comparar quaisquer valores de campo.Registro (record)
Book(String title, int year) compara ambos os componentes e fornece os métodos de acesso title() e year().Resultado da busca
S
Simetria
Sobrecarga
equals(Book other) cria uma sobrecarga e mantém equals(Object) disponível para as coleções.Sobrescrita
equals(Object) em uma nova classe.@Override permite que o compilador verifique se o método de igualdade de um livro é uma sobrescrita válida.T
Tipo de referência
null.String é um tipo de referência, então uma variável pode se referir a um objeto de texto ou não conter uma referência a objeto.Tipo primitivo
int permite que count armazene 12 diretamente, então atribuí-lo a outra variável copia 12.Transitividade
V
Validação de dados
Valor de retorno
findByName de um cadastro pode apontar para uma pessoa armazenada que o chamador e o cadastro passam a compartilhar.Valor padrão do campo
ArrayList<Song> não inicializado é null, então ainda não existe uma lista.Verificação direta
- 1Primeiros programas em Java
- 2Condições e laços em Java
- 3Métodos em Java
- 4Listas e vetores em Java
- 5Classes e objetos em Java
- 6Objetos e referências em Java
- 7HashMaps e coleções em Java
- 8Herança, interfaces e polimorfismo em Java
- 9Exceções em Java
- 10Pacotes e testes unitários em Java
- 11Genéricos e expressões lambda em Java
Sobre esta aula
Crie seus próprios Materiais de Estudo
Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.