- Aprender
- Computação e TI
- Programação
- Programação orientada a objetos com Java
- Classes e objetos em Java
Classes e objetos em Java
toString legível e termine projetando, implementando e testando uma classe completa a partir de uma breve descrição.BásicoAula 5 de 11 do curso Curso de Programação orientada a objetos com Java
O que você vai aprender
- Explique como uma classe define um novo tipo de dado com seu próprio comportamento e crie e use objetos desse tipo em um programa.
- Explique o que são uma classe e um objetoDescreva uma classe como a definição de um tipo, um objeto como uma instância desse tipo e o estado e o comportamento como aquilo que os objetos armazenam e fazem.
- Crie objetos e chame seus métodosCrie objetos com
new, chame seus métodos com a notação de ponto e preveja como objetos distintos se comportam.
- Explique o que são uma classe e um objeto
- Declare as variáveis de instância que armazenam o estado de um objeto e escreva construtores que deem valores iniciais válidos a cada novo objeto.
- Declare variáveis de instânciaDeclare campos com tipos adequados, conheça seus valores padrão e distinga-os de variáveis locais e parâmetros.
- Escreva construtores que inicializem objetosEscreva construtores que definam cada campo, use
thispara distinguir campos de parâmetros e evite erros que deixam campos sem valor definido. - Disponibilize vários construtores e encadeie-osFaça a sobrecarga de construtores para diferentes formas de criar um objeto e use
this(...)para escrever o código de inicialização uma única vez.
- Declare variáveis de instância
- Escreva métodos de instância que leiam e alterem o estado de um objeto, acompanhe seus efeitos e decida quando algo pertence a um objeto ou à classe como um todo.
- Escreva métodos de instância que usem e alterem o estadoEscreva métodos que informem ou alterem os campos de um objeto, chame outros métodos do mesmo objeto e acompanhe o estado dos objetos passo a passo.
- Distinga membros de instância de membros estáticosUse
thispara o objeto atual, decida o que deve ser estático e corrija os erros causados pela mistura de código estático e de instância.
- Escreva métodos de instância que usem e alterem o estado
- Oculte campos com modificadores de acesso, forneça acesso controlado por meio de métodos de acesso, métodos modificadores e métodos com funções claras, e mantenha cada objeto em um estado válido.
- Oculte dados com modificadores de acessoUse
private, acesso de pacote,protectedepublicpara controlar qual código pode usar os membros de uma classe e explique por que os campos são mantidos privados. - Escreva métodos de acesso e métodos modificadoresEscreva métodos de acesso e modificadores com a nomenclatura padrão e decida quais campos precisam deles e quando um método com função clara é melhor.
- Mantenha os objetos válidos por meio de verificaçõesDefina as regras que o estado de um objeto deve seguir, aplique-as em construtores e métodos e evite fornecer dados internos que permitam contornar essas verificações.
- Oculte dados com modificadores de acesso
- Dê aos objetos uma forma textual legível com
toStringe depois projete, escreva e teste uma classe completa a partir de uma breve descrição.- Escreva um método toStringEscreva um método
toStringque retorne uma descrição legível de um objeto e saiba quando o Java o chama automaticamente. - Projete, implemente e teste uma classe completaTransforme uma breve descrição em uma classe com campos, construtores, métodos e regras e depois teste cada comportamento a partir de um programa com main.
- Escreva um método toString
Guia de estudo de Classes e objetos em Java
1Classes, objetos e chamadas de métodos
Um programa muitas vezes precisa manter vários valores relacionados juntos: o nome, a espécie e a idade de um animal de estimação, por exemplo. Uma classe dá um tipo a esse grupo e reúne em um só lugar as ações que o utilizam. Você relacionará definições de classes aos objetos criados durante a execução de um programa e depois acompanhará chamadas de métodos para ver os dados de qual objeto mudam. A distinção entre classe, objeto e variável ajudará você a ler o código que escreverá a seguir.
1.1 Um tipo com estado e comportamento
Uma classe define um tipo: os dados que seus objetos lembram e as operações que oferecem. Um objeto, também chamado de instância, é um valor desse tipo criado durante a execução de um programa. Escrever uma definição de classe não cria um objeto.
Estado e comportamento
O nome, a espécie e a idade de um animal de estimação formam seu estado. Esses valores atuais ficam em variáveis de instância, também chamadas de campos. Uma operação como celebrar um aniversário é um comportamento, escrito como método. O método de aniversário fica junto da idade que ele altera, de modo que a regra é mantida em um só lugar.
Pense em uma classe como uma planta de construção. Muitas casas podem seguir uma planta, mas cada casa tem seu próprio conteúdo. Da mesma forma, dois animais podem seguir a mesma definição e ter idades diferentes. Você escreve os nomes dos campos uma vez; cada objeto recebe seus próprios valores.
Leia uma classe pequena
public class Pet {
private String name;
private String species;
private int age;
public Pet(String name, String species, int age) {
this.name = name;
this.species = species;
this.age = age;
}
public void celebrateBirthday() {
this.age++;
}
}
Leia de fora para dentro. public class Pet apresenta o tipo, e as chaves externas contêm sua definição. As três primeiras declarações descrevem o estado. O bloco chamado Pet é um construtor: ele dá a um novo objeto seus primeiros valores. celebrateBirthday é um método: ele aumenta a idade do objeto atual. As palavras private e public controlam o acesso; exemplos posteriores as usam para proteger o estado.
Os parâmetros do construtor são valores fornecidos quando um objeto é criado. Em this.age = age;, o lado esquerdo indica o campo do objeto e o lado direito indica o parâmetro fornecido. O construtor armazena essa entrada para que o objeto possa lembrá-la após o fim da construção.
Coloque uma classe em uso
Você já usou classes de biblioteca. Scanner descreve objetos que leem dados de entrada, ArrayList descreve listas e um objeto String oferece operações como length(). Uma classe que você escreve segue a mesma ideia básica: ela disponibiliza um tipo útil para o restante do programa.
Sem Pet, um programa poderia passar nome, espécie e idade separadamente para muitos métodos. Ele precisa manter cada grupo unido corretamente. Um objeto Pet reúne os valores relacionados em uma unidade. Os métodos desse objeto sabem qual idade devem usar.
Organize e nomeie o código
Em arquivos de código-fonte comuns, uma classe pública chamada Pet é salva em Pet.java, com as mesmas letras maiúsculas e minúsculas. Uma classe Main separada pode conter o método main do programa, criar animais e chamar seus métodos. Pet não precisa de seu próprio main. Classes no mesmo pacote sem nome podem usar umas às outras sem importações; os exemplos simples mantêm essas classes em uma pasta.
Use substantivos em UpperCamelCase para classes, como LibraryBook. Use lowerCamelCase para campos e métodos: pageCount nomeia dados; giveBack() nomeia uma ação. Essas convenções ajudam o leitor a reconhecer cada parte, embora o compilador permita muitos outros nomes.
Principais conclusões
- Uma classe define um tipo; um objeto é uma instância criada durante a execução do programa.
- Campos armazenam o estado, e métodos fornecem comportamento.
- Objetos de uma classe seguem a mesma definição e armazenam seus próprios valores nos campos de instância.
- Uma classe pública em um arquivo de código-fonte comum usa o nome correspondente da classe como nome do arquivo.
1.2 Novos objetos e as referências que os acessam
Criar um objeto e declarar uma variável são operações diferentes. Uma variável fornece um nome pelo qual acessar um objeto; new cria o objeto ao qual o nome se referirá.
Leia uma instrução de criação de objeto
Pet luna = new Pet("Luna", "cat", 3);
Pet é o tipo da variável, e luna é seu nome. new Pet(...) cria um novo objeto e chama um construtor correspondente com três argumentos. O construtor armazena o nome, a espécie e a idade. A referência resultante é atribuída a luna.
Uma referência informa ao Java qual objeto usar. É útil desenhar uma variável como um nome com uma seta apontando para um objeto. A variável não é uma segunda cópia de todos os campos do objeto.
Chame um método em um objeto específico
O ponto em luna.celebrateBirthday() conecta uma referência a um método a ser executado em seu objeto. Um método que retorna um valor pode fornecê-lo a outra expressão:
luna.celebrateBirthday();
int years = luna.getAge();
System.out.println(years); // 4
Aqui getAge() é um método público que retorna a idade do animal. A chamada de aniversário altera o estado; o método de acesso lê esse estado. Pet.celebrateBirthday() falha porque esse método de instância precisa de um animal específico. O nome da classe, sozinho, não responde de quem é o aniversário.
Objetos distintos e referências compartilhadas
Pet max = new Pet("Max", "dog", 5);
luna.celebrateBirthday();
System.out.println(luna.getAge()); // 5
System.out.println(max.getAge()); // 5
Luna já tinha 4 anos, portanto o próximo aniversário a leva a 5. Max continua com 5. Idades iguais não fazem deles o mesmo objeto: as duas expressões new criaram animais distintos.
Agora considere uma atribuição sem new:
Pet other = luna;
other.celebrateBirthday();
System.out.println(luna.getAge()); // 6
A atribuição copia a referência de Luna. Ambos os nomes acessam um único animal, portanto alterá-lo por meio de other é visível por meio de luna. Para criar um segundo animal, chame new novamente.
Faça os argumentos corresponderem aos parâmetros do construtor
Os argumentos devem corresponder à quantidade, à ordem e aos tipos dos parâmetros de um construtor declarado. Se o único construtor disponível aceitar (String, String, int), new Pet() não compila. O Java não adivinha valores ausentes. Trocar as duas cadeias de caracteres, porém, ainda compila: new Pet("cat", "Luna", 3) fornece os tipos certos, mas os significados errados. Leia os nomes dos parâmetros para verificar essa ordem.
Estas duas tentativas incompletas falham por motivos diferentes:
Pet a; // declaration only
// System.out.println(a.getAge()); // a has no assigned value
// Pet b = Pet("Bo", "cat", 2); // missing new
Uma variável local deve receber um valor antes de ser usada. Sem new, o Java procura um método chamado Pet, não uma expressão de criação de objeto. Depois que um objeto existe, exibi-lo pode mostrar um texto como Pet@8bcc55f até que você forneça um método toString() legível; essa saída não significa que a construção falhou.
Principais conclusões
- Use new com argumentos que correspondam a um construtor disponível.
- A notação de ponto chama um método de instância no objeto acessado por uma referência.
- Outra expressão new cria outro objeto; a atribuição de uma referência compartilha um objeto existente.
- Argumentos do mesmo tipo podem ser trocados sem erro de compilação, portanto confira seus significados.
- Uma declaração local, por si só, não cria objeto e não dá à variável um valor utilizável.
2Campos e o estado inicial de um objeto
Objetos precisam de um lugar para guardar seus dados e de uma maneira confiável de receber seus primeiros valores. Campos fornecem o armazenamento; construtores inicializam cada novo objeto. Você escolherá tipos de campos, separará o estado duradouro do trabalho local temporário e acompanhará a construção desde os valores padrão até um objeto pronto. Depois, oferecerá várias formas úteis de criar o mesmo tipo de objeto, mantendo a inicialização e as verificações compartilhadas em um só lugar.
2.1 Dados que um objeto lembra
Um objeto precisa de um campo para cada valor que deve lembrar entre chamadas de métodos. Escolha esses valores antes de escrever os métodos: eles formam o estado duradouro do objeto.
Declare campos com tipos adequados
Um campo fica dentro das chaves da classe, mas fora de todos os construtores e métodos. Uma declaração precisa de um tipo e um nome; um modificador de acesso e um inicializador também podem aparecer.
private String title;
private int pageCount;
private boolean lentOut = false;
String é adequado para texto, int para uma contagem inteira e boolean para uma condição de sim ou não. Campos também podem usar tipos de classe, como ArrayList<String>. Estes exemplos usam private; omitir o modificador resulta em acesso de pacote, não em acesso privado.
Cada objeto recebe uma cópia separada de cada campo de instância. Dois objetos Rectangle têm, cada um, um width e um height, embora as declarações apareçam apenas uma vez na classe.
Valores padrão nem sempre são valores iniciais utilizáveis
| Tipo do campo | Valor padrão |
|---|---|
int, long | 0 |
double | 0.0 |
boolean | false |
Tipos de classe, incluindo String e ArrayList | null |
Um valor padrão é atribuído a um campo quando o espaço de armazenamento do objeto é criado. Ele pode não ser adequado à finalidade do objeto. Um campo de lista com valor null não é uma lista vazia: você ainda precisa criar a lista antes de adicionar um item.
Variáveis locais não recebem esse valor padrão. Dentro de um método, int total; seguido de System.out.println(total); não compila porque nenhum valor foi atribuído a total. Mantenha separadas a regra dos campos e a das variáveis locais.
Decida o que deve durar
| Tipo de variável | Onde é declarada | Para que serve |
|---|---|---|
| Campo de instância | Na classe, fora dos métodos | Valores lembrados por um objeto |
| Parâmetro | No cabeçalho de um construtor ou método | Entradas para essa chamada |
| Variável local | Dentro de um bloco de método ou construtor | Trabalho temporário para essa chamada |
Um campo permanece com seu objeto. Um parâmetro fica disponível durante sua chamada, e uma variável local fica disponível em seu bloco enquanto essa chamada é executada. Se um contador precisa manter sua contagem de uma chamada para a seguinte, a contagem pertence a um campo. Um subtotal temporário usado em um cálculo pode ser local.
Armazene as entradas; calcule os valores derivados
public class Rectangle {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double area() {
return this.width * this.height;
}
}
As dimensões estão em centímetros, portanto area() retorna uma área em centímetros quadrados. Armazenar um campo separado para a área criaria outro valor a atualizar sempre que uma das dimensões mudasse. Calculá-la a partir das dimensões mantém o resultado consistente.
Use um campo de instância final para um valor atribuído uma vez, como private final String title;. Dê a ele um valor em sua declaração ou em cada construtor. final impede a reatribuição. static tem outro significado: cria um campo de classe compartilhado. Adicionar static ao nome de um animal faria animais diferentes compartilharem uma única variável de nome.
Principais conclusões
- Campos de instância são declarados fora dos métodos e armazenam o estado de cada objeto.
- Campos recebem valores padrão; variáveis locais devem receber valores antes do uso.
- Escolha campos para dados duradouros e variáveis locais para trabalho temporário.
- Calcule valores derivados dos campos armazenados para evitar cópias inconsistentes.
- Um campo de instância final recebe um valor uma vez; um campo estático é compartilhado pela classe.
- Um campo de lista com null ainda precisa de um objeto de lista antes de poder ser usado.
2.2 Dos argumentos aos campos inicializados
Um construtor inicializa um objeto quando new o cria. Seu nome deve corresponder ao da classe, e ele não tem tipo de retorno. Até mesmo void transformaria a declaração em um método.
Transfira as entradas para o estado
public Pet(String name, String species, int age) {
this.name = name;
this.species = species;
this.age = age;
}
Um construtor geralmente recebe entradas como parâmetros e as armazena em campos. Aqui cada parâmetro tem o mesmo nome de seu campo. O nome age sozinho se refere ao parâmetro; this.age se refere ao campo do objeto que está sendo criado. A atribuição lê o valor fornecido à direita e o escreve no campo à esquerda.
Para new Pet("Luna", "cat", 3), primeiro os campos do novo objeto recebem valores padrão: null, null e 0. O construtor então recebe os três argumentos e substitui esses valores padrão. Quando a construção termina, new produz a referência que o código que o chamou armazena.
Dois defeitos que compilam
public Pet(String name, String species, int age) {
name = name;
this.species = species;
this.age = age;
}
As duas ocorrências de name se referem ao parâmetro, portanto a primeira linha atribui esse parâmetro a si mesmo. O nome do objeto permanece null. O código é válido, por isso é importante verificar os valores do novo objeto.
Uma declaração dentro do construtor causa um problema relacionado:
String nick = initialNick;
Se um campo chamado nick já existir, essa linha cria uma variável local com o mesmo nome. A variável local desaparece quando a construção termina. Escreva this.nick = initialNick; para armazenar a entrada no campo; não repita o tipo.
Saiba quando existe um construtor sem argumentos
Se uma classe não declarar construtores, o Java fornece um construtor padrão sem parâmetros. Para uma classe simples, seus campos mantêm os valores dos inicializadores declarados ou seus valores padrão. Assim que você declara um construtor, o Java deixa de fornecer o padrão.
Uma classe que tem apenas Pet(String name, String species, int age) não pode, portanto, ser criada com new Pet(). Adicione um construtor explícito sem argumentos apenas quando houver um estado inicial útil para ele. Observe também public void Pet(...): isso é um método, portanto não fornece o construtor pretendido.
Defina também os valores internos iniciais
Nem todo campo precisa vir de um argumento. Um novo livro sempre pode começar com this.lentOut = false;. Uma classe que possui uma lista pode inicializá-la assim, com ArrayList importado:
private ArrayList<String> tracks;
public Playlist() {
this.tracks = new ArrayList<>();
}
A lista vazia está pronta para add. Deixar tracks como null faz uma chamada posterior a tracks.add(...) falhar com NullPointerException.
Um inicializador de campo é executado antes do corpo do construtor. Se private int score = 10; for seguido de this.score = 5; no construtor, a pontuação do objeto pronto será 5. Teste os valores após a construção, não apenas os valores escritos junto às declarações dos campos. Os construtores também precisam das mesmas regras de validação usadas em alterações posteriores, para que o estado inicial seja válido.
Principais conclusões
- Um construtor tem o nome da classe e não tem tipo de retorno.
- Use this.field = parameter quando os nomes forem iguais.
- Nem a atribuição de um parâmetro a si mesmo nem uma nova variável local inicializa o campo pretendido.
- O Java fornece um construtor padrão apenas quando a classe não declara construtores.
- Inicialize o estado interno, como listas vazias, e aplique a validação durante a construção.
2.3 Várias formas de construir um tipo de objeto
Uma classe pode oferecer várias formas úteis de criar um objeto. Sobrecarga de construtores significa declarar construtores com listas de parâmetros diferentes.
Distinga construtores pelos parâmetros
O Java distingue construtores pela quantidade, pelos tipos e pela ordem de seus parâmetros. Os nomes dos parâmetros não os distinguem. Box(String label) e Box(String name) descrevem a mesma lista de parâmetros, portanto não podem ser declarados juntos em uma classe.
Um animal pode ser criado com uma idade ou como recém-nascido:
public Pet(String name, String species, int age) {
this.name = name;
this.species = species;
this.age = age;
}
public Pet(String name, String species) {
this(name, species, 0);
}
new Pet("Pip", "rabbit") seleciona o construtor com dois parâmetros. Esse construtor fornece 0 como a idade ausente e chama o construtor com três parâmetros. O novo objeto termina com o estado "Pip", "rabbit" e 0.
Encadeie a inicialização no mesmo objeto
Dentro de um construtor, this(...) chama outro construtor da mesma classe. O construtor mais curto escolhe os valores padrão; o mais completo faz a inicialização compartilhada. As verificações colocadas nessa inicialização se aplicam às duas formas de construção.
Compare duas linhas que parecem semelhantes:
| Linha dentro de um construtor | Efeito |
|---|---|
this(name, species, 0); | Inicializa o objeto atual por meio de outro construtor |
new Pet(name, species, 0); | Cria um animal separado |
A segunda linha não preenche os campos do objeto atual. Se seu resultado nunca for armazenado, esse animal extra será descartado. O animal atual ainda pode ter campos null.
Acompanhe uma cadeia antes de rastrear o código posterior
Um relógio pode oferecer a meia-noite como padrão:
public Clock() {
this(0, 0);
}
public Clock(int hours, int minutes) {
this.hours = hours;
this.minutes = minutes;
}
Para new Clock(), entre no construtor sem argumentos, siga this(0, 0) e atribua valores aos dois campos no construtor com dois argumentos. Depois, o controle retorna ao construtor sem argumentos. Isso cria um relógio, não dois. Uma chamada a new Clock(9, 5) começa diretamente no construtor mais completo.
Mantenha a cadeia válida e clara
Um construtor pode conter no máximo uma chamada this(...). A cadeia deve chegar a uma inicialização que não chame de volta a cadeia: dois construtores que delegam um ao outro causam um erro de compilação de construtor recursivo.
Colocar this(...) em primeiro lugar é um estilo claro que funciona em várias versões do Java. No Java 24 e em versões anteriores sem recursos de prévia, ela deve vir primeiro. O Java 25 permite instruções antes dela para tarefas como calcular ou verificar argumentos. Essas instruções não podem ler os campos de instância do objeto atual nem chamar seus métodos de instância; são permitidas atribuições a campos declarados na classe sem inicializadores na declaração.
Ofereça sobrecargas porque os códigos que chamam os construtores têm diferentes entradas úteis. Um relógio à meia-noite faz sentido; um livro sem título talvez não faça. A existência de um construtor completo não cria automaticamente sobrecargas mais curtas.
Principais conclusões
- Construtores sobrecarregados diferem pelas listas de parâmetros, não pelos nomes dos parâmetros.
- A chamada this(...) delega a outro construtor no mesmo objeto.
- Mantenha a inicialização e as verificações compartilhadas em uma única forma de construção.
- Uma cadeia de construtores não deve retornar a um construtor que já esteja nessa cadeia.
3Métodos que leem e alteram o estado
Um objeto útil pode responder a perguntas sobre si mesmo e executar ações que alteram seu estado. Métodos de instância colocam essas operações ao lado dos campos que usam. Você acompanhará alterações uma instrução por vez, tratará a virada de um relógio e retornará valores que outros códigos possam usar. Você também decidirá quais dados pertencem a cada objeto e quais dados ou operações pertencem à classe, para que objetos distintos permaneçam separados.
3.1 Cálculos, alterações e rastreamento de estado
Um método de instância é executado para um objeto específico. Declare-o sem static e use os campos desse objeto por meio de nomes como this.width. Decida do que o código que o chama precisa: uma alteração de estado, um resultado retornado ou ambos.
Retorne cálculos; deixe alterações de estado explícitas
Para um retângulo cujas dimensões são armazenadas em centímetros:
public double area() {
return this.width * this.height;
}
public double perimeter() {
return 2 * (this.width + this.height);
}
public void scale(double factor) {
this.width = this.width * factor;
this.height = this.height * factor;
}
Um retângulo com lados de 3,0 e 4,0 tem área de 12,0 centímetros quadrados e perímetro de 14,0 centímetros. scale(2) altera os lados para 6,0 e 8,0. As chamadas seguintes retornam área 48,0 e perímetro 28,0. Cada método lê os campos atuais, portanto não é necessário atualizar uma área armazenada.
area() retorna um número que pode ser armazenado ou usado em um cálculo. Se apenas exibisse o número e tivesse tipo de retorno void, o código que o chama não poderia escrever double twice = r.area() * 2;. Deixe a exibição para o código que chama o método quando a função dele for calcular um valor.
Atualize campos relacionados em ordem
O método advance() de um relógio deve tratar a virada dos minutos e das horas:
public void advance() {
this.minutes++;
if (this.minutes == 60) {
this.minutes = 0;
this.hours++;
if (this.hours == 24) {
this.hours = 0;
}
}
}
Às 23:58, a primeira chamada chega a 23:59 e pula os dois blocos de reinicialização. A chamada seguinte chega por um instante ao minuto 60, volta-o para 0, aumenta a hora para 24 e a volta para 0. O estado final é 00:00. Mais uma chamada resulta em 00:01.
Rastreie objetos separadamente
Escreva uma linha após cada instrução e uma coluna para os campos de cada objeto. Altere apenas o objeto indicado pelo objeto receptor da chamada. Para animais com idades 2 e 7, dois aniversários no primeiro animal e setAge(1) no segundo deixam as idades em 4 e 1. Não execute o corpo de um método para todas as instâncias só porque elas compartilham uma classe.
Uma declaração local pode ocultar o campo que você pretendia atualizar. public void reset() { int count = 0; } apenas cria uma variável temporária. this.count = 0; reinicia a contagem duradoura.
Reutilize métodos e complete os caminhos de retorno
Dentro de um método de instância, area() e this.area() chamam o método de área do mesmo objeto. Um método de descrição pode reutilizar esse cálculo em vez de copiar sua expressão. Um método também pode alterar o estado e retornar se a operação foi bem-sucedida, como lend() faz ao marcar um livro como emprestado e informar true.
Um método que não seja void não pode simplesmente chegar ao fim sem retornar um valor compatível. Se get() retorna apenas dentro de if (count > 0), o que acontece quando a contagem é 0? Adicione o caminho de retorno necessário ou lance uma exceção para um valor que o método rejeite. Um caminho que lança uma exceção não retorna ao código que chamou o método e não precisa de um valor de retorno.
Principais conclusões
- Métodos de instância leem ou alteram o estado do objeto no qual são chamados.
- Retorne valores calculados para que os códigos que chamam os métodos possam usá-los em expressões.
- Rastreie as instruções em ordem, mantendo separados os campos de cada objeto.
- Métodos que tratam viradas devem fazer todos os campos relacionados voltarem ao intervalo permitido.
- Reutilize outros métodos do mesmo objeto chamando-os diretamente ou com this.
- Um método que retorna um valor precisa fornecê-lo em cada caminho que retorna normalmente.
3.2 Membros de cada objeto e membros compartilhados da classe
Pergunte a quem pertence um valor ou uma operação antes de decidir se deve usar static. A idade de um animal pertence a um animal. A contagem de todos os animais criados pertence à classe como um todo.
O objeto atual é this
Dentro de um construtor ou método de instância, this se refere ao objeto que está sendo construído ou recebendo a chamada. Em this.age++, ele identifica de quem é a idade que muda. Em this.name = name, ele distingue o campo de um parâmetro com o mesmo nome. this(...) é a forma de encadeamento de construtores.
Um método estático não tem objeto atual, portanto this não pode ser usado nele. Ele ainda pode criar um objeto ou receber uma referência e usá-la para chamar métodos de instância.
Campos compartilhados e campos de cada objeto
public class Counter {
private int count;
private static int created = 0;
public Counter() {
this.count = 0;
created++;
}
public void increment() {
this.count++;
}
public int getCount() {
return this.count;
}
public static int getCreated() {
return created;
}
}
Após duas chamadas a new Counter(), há dois campos count separados, ambos inicialmente 0, e um campo created igual a 2. Incrementar o primeiro contador altera sua contagem para 1; o segundo permanece em 0. Counter.getCreated() ainda retorna 2 porque nenhum novo objeto foi construído.
Trabalhe por meio de um objeto a partir de main
public static void main(String[] args) {
Counter c = new Counter();
c.increment();
System.out.println(c.getCount());
}
O método main clássico é estático. Uma chamada a increment() apenas pelo nome nesse método não teria um objeto alvo e não compilaria. A variável c fornece esse alvo. Tornar count estático para eliminar o erro mudaria o projeto: todos os contadores compartilhariam uma única contagem.
| Tipo de método | Acesso direto disponível |
|---|---|
| Método de instância | Seus membros de instância e os membros estáticos da classe |
| Método estático | Membros estáticos; membros de instância por meio de uma referência a objeto |
O Java 25 também aceita formas de main como método de instância, mas o cabeçalho estático clássico continua válido. Estes exemplos o usam de forma consistente.
Escolha métodos e constantes com cuidado
Uma operação que precisa dos campos de um objeto deve ser um método de instância. Uma operação que depende apenas de seus parâmetros pode ser estática, como um método auxiliar que converte um número total de minutos em horas. Chamadas estáticas normalmente usam o nome da classe: Math.sqrt(16) ou Counter.getCreated().
Um valor fixo compartilhado geralmente é declarado como constante:
public static final int MAX_CELSIUS = 30;
public o torna disponível para os códigos que o usam, static dá à classe um campo compartilhado, e final impede a reatribuição. UPPER_SNAKE_CASE indica uma constante por convenção. Essa é uma exceção comum à regra de tornar campos privados. Não use static para nomes, dimensões ou outros estados comuns que precisam ser diferentes entre objetos.
Principais conclusões
- A referência this identifica o objeto atual dentro de código de instância.
- Membros estáticos pertencem à classe; membros de instância pertencem a objetos individuais.
- Um método estático precisa de uma referência a objeto para usar membros de instância.
- Corrija erros de contexto estático fornecendo o objeto adequado quando o estado pertencer a um objeto.
- Um campo public static final normalmente fornece uma constante compartilhada.
4Acesso controlado e estado válido
Uma classe deve controlar os valores que podem entrar em seu estado. Campos privados e métodos públicos permitem que a classe verifique cada alteração e ofereça operações úteis aos códigos que a usam. Você escolherá níveis de acesso, escreverá métodos de acesso e modificadores quando forem úteis e protegerá regras como um intervalo de temperatura permitido. Você também verá por que uma lista privada ainda pode ser exposta por um método de acesso e como impedir que código externo contorne as verificações da classe.
4.1 Estado privado e interface pública
Encapsulamento mantém o estado de um objeto junto com o código que o controla. Campos geralmente são privados, enquanto os códigos que usam o objeto recorrem a operações públicas. Isso oculta os detalhes de armazenamento e dá à classe um lugar para aplicar suas regras.
Escolha um nível de acesso
| Acesso | Quem pode usar o membro |
|---|---|
private | Código na mesma classe |
| Sem modificador | Código no mesmo pacote |
protected | O mesmo pacote, além do acesso por subclasses segundo as regras de subclasses do Java |
public | Código que pode acessar o tipo que declara o membro |
Um pacote agrupa classes. Os exemplos simples colocam classes comuns juntas sem uma declaração package, em um pacote sem nome. O acesso de pacote é o resultado da omissão do modificador; não existe uma palavra-chave de acesso package para adicionar a um campo.
Comece com o acesso mais restrito que seja útil. Torne os campos privados, os construtores e as operações necessários a outras classes públicos e os métodos auxiliares internos privados. Constantes públicas são uma exceção comum para campos.
Deixe a classe controlar as alterações
Se Pet declarar private int age;, uma classe Main separada não poderá atribuir luna.age = -5;. O compilador rejeita o acesso. Uma chamada como luna.setAge(-5) pode ser permitida, pois o método público pertence a Pet e pode decidir se armazena o argumento.
Um campo público permitiria que qualquer código contornasse esse método. Quando todas as alterações passam pelo código da classe, as verificações podem ficar em um só lugar. O acesso privado, por si só, não valida uma idade; os métodos também devem fazer as verificações.
Mantenha a interface pública estável
Suponha que um relógio inicialmente armazene campos hours e minutes separados. Os códigos que o usam chamam getHours() e getMinutes(). Depois você substitui os campos por um único valor minutesSinceMidnight:
public int getHours() {
return this.minutesSinceMidnight / 60;
}
public int getMinutes() {
return this.minutesSinceMidnight % 60;
}
Com 615 minutos armazenados, a divisão retorna 10 e o resto retorna 15. O relógio informa 10:15 por meio dos mesmos métodos. Os códigos que o usam não precisam ser reescritos porque dependiam das operações públicas, não de como o estado era armazenado. Essa liberdade de alterar detalhes internos é a ocultação de informações.
O acesso pertence às classes, não a objetos individuais
Código dentro de Pet pode ler um campo privado de outro objeto Pet. A permissão de acesso decorre de estar dentro da classe. Uma classe de nível superior diferente não recebe permissão por ter uma referência a um animal ou por estar na mesma pasta.
Para declarações comuns de classes de nível superior, as opções de acesso são public ou nenhum modificador. Uma declaração private class Pet no nível superior é ilegal. Uma classe pública de nível superior fica no arquivo de código-fonte correspondente, e um arquivo de código-fonte comum contém no máximo uma classe pública de nível superior.
Campos privados são um limite de projeto do programa. Eles impedem que outro código comum dependa diretamente dos campos internos, mas não são uma forma de manter um valor em segredo de alguém que pode inspecionar o programa. Use-os para controlar a interface da sua classe e proteger seu estado.
Principais conclusões
- Campos privados e operações públicas mantêm o controle do estado dentro da classe.
- Omitir um modificador de acesso resulta em acesso de pacote.
- Métodos públicos permitem alterar o armazenamento interno sem alterar os códigos que os chamam.
- O acesso privado é verificado por classe, portanto código dentro da classe pode acessar os campos de outra instância.
- Classes comuns de nível superior usam acesso público ou de pacote.
4.2 Métodos de acesso adequados à classe
Uma classe pode permitir que os códigos que a usam leiam ou alterem partes selecionadas de seu estado por meio de métodos. Decida quais operações a classe deve oferecer antes de criar métodos de acesso para todos os campos.
Retorne valores com métodos de acesso
Um método de acesso, também chamado de método acessor, retorna informações sem alterar o objeto. Um método de acesso comum lê um campo:
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
O tipo retornado corresponde à informação fornecida. Um método de acesso que exibe o valor e retorna void não pode fornecer um valor a uma expressão como pet.getAge() + 1. O código que chama o método deve receber o valor e decidir como usá-lo.
Altere valores selecionados com métodos modificadores
Um método modificador, também chamado de método mutador, recebe um novo valor e atualiza o estado:
public void setName(String name) {
this.name = name;
}
this.name é o campo. name é o parâmetro. Escrever name = name; compila, mas não altera nenhum campo. Não há valor de retorno aqui porque a função desse método modificador é fazer uma alteração.
Para um animal com idade 3:
luna.setName("Lulu");
System.out.println(luna.getName() + " is " + luna.getAge());
// Lulu is 3
O código que chama os métodos nunca acessa um campo diretamente. O método modificador altera o nome, e os métodos de acesso retornam os valores atuais para que o código os combine.
Siga os nomes esperados por outros códigos Java
| Campo | Método de acesso | Método modificador, se necessário |
|---|---|---|
pageCount | getPageCount() | setPageCount(int pageCount) |
name | getName() | setName(String name) |
lentOut | isLentOut() | setLentOut(boolean lentOut) |
Coloque em maiúscula a primeira letra do nome do campo depois de get, set ou do prefixo is, usual para booleanos. Um nome como getname() pode compilar, mas não segue a convenção esperada por ferramentas e outros programadores.
Disponibilize apenas operações úteis
O título de um livro pode ter um método de acesso e nenhum método modificador, pois os códigos que o usam devem poder lê-lo, mas não substituí-lo. Um campo privado final pode garantir que ele receba um valor uma única vez. Um contador interno pode não precisar de nenhum dos dois métodos se apenas a classe o usar.
Um método modificador sem verificação pode aceitar o mesmo valor prejudicial que um campo público. Adicione verificações quando uma alteração tiver restrições. Considere também um método de ação: celebrateBirthday() expressa o aumento da idade em uma unidade, e lend() expressa uma tentativa de empréstimo. lend() pode examinar o estado atual e rejeitar um segundo empréstimo. Um método modificador também pode fazer essa verificação; o nome de ação torna a operação mais clara para quem a chama.
Um método de acesso pode calcular uma resposta. getArea() pode retornar o produto da largura pela altura sem nenhum campo area.
Principais conclusões
- Um método de acesso retorna informações; um método modificador altera um valor selecionado.
- Siga o padrão de nomenclatura getName, setName e o usual isName para booleanos.
- Forneça aos campos apenas os métodos de acesso e modificadores necessários aos códigos que os usam.
- Métodos de ação como lend expressam claramente o comportamento permitido e podem verificar transições.
4.3 Regras preservadas em cada alteração
Uma invariante é uma regra sobre o estado de um objeto que é verdadeira após a construção e após cada operação pública. Um termostato pode permitir temperaturas desejadas de 5 °C a 30 °C. Toda forma de definir essa temperatura deve preservar esse intervalo.
Escolha o que acontece com uma entrada inválida
Três respostas úteis são manter o valor anterior, limitar o valor ao intervalo ou lançar uma exceção. Elas têm efeitos diferentes, portanto escolha uma resposta clara para cada operação.
| Resposta | Resultado de uma chamada inválida |
|---|---|
| Ignorar | O valor anterior do campo permanece inalterado |
| Limitar ao intervalo | O limite permitido mais próximo é armazenado |
| Lançar uma exceção | A chamada é interrompida com um erro como IllegalArgumentException |
Para uma idade não negativa de um animal, um método modificador que ignora valores inválidos pode atribuir o valor apenas dentro de if (age >= 0). Se a idade armazenada for 4, setAge(-2) a mantém em 4. Ele não muda a idade para 0.
Coloque as verificações de intervalo dentro da classe
public static final int MIN_CELSIUS = 5;
public static final int MAX_CELSIUS = 30;
private int target;
public void setTarget(int celsius) {
if (celsius < MIN_CELSIUS) {
this.target = MIN_CELSIUS;
} else if (celsius > MAX_CELSIUS) {
this.target = MAX_CELSIUS;
} else {
this.target = celsius;
}
}
public void raise() {
setTarget(this.target + 1);
}
Começando em 20 °C, setTarget(35) armazena 30. setTarget(-3) armazena 5. Após setTarget(29), uma chamada a raise() armazena 30; outra chamada mantém o valor em 30. Reutilizar o método modificador impede que raise() ultrapasse o limite superior.
Verificações apenas em Main não podem proteger o termostato. Outro código que o use poderia esquecê-las. A classe deve verificar a entrada no ponto em que controla o armazenamento.
Valide também o estado inicial
public Thermostat(int target) {
setTarget(target);
}
Com esse construtor que limita o valor ao intervalo, new Thermostat(50) termina em 30 °C. Atribuir this.target = target; diretamente permitiria o estado inicial inválido 50. Um método privado de verificação compartilhado é outra forma de reutilizar a validação em construtores e métodos posteriores.
Lançar uma exceção é útil quando um argumento inválido indica um erro de programação. Por exemplo, um construtor pode rejeitar uma contagem negativa com throw new IllegalArgumentException("count must not be negative");. O objeto não é construído com sucesso, portanto nenhum objeto concluído inválido é entregue ao código que chamou o construtor. Ignorar a entrada é mais simples, mas silencioso; o código que chamou o método pode precisar de outra forma de perceber que nada mudou.
Mantenha os dados internos mutáveis dentro da classe
Uma lista privada ainda pode ser exposta se um método de acesso retornar a lista original. O código que chamou o método poderia então adicionar ou remover entradas sem usar os métodos da classe, contornando uma regra de tamanho máximo.
Para um campo ArrayList<String> chamado tracks, return new ArrayList<>(this.tracks); fornece uma lista separada que contém as mesmas cadeias de caracteres. Alterar a lista retornada não altera a lista interna. Operações como addTrack(String title) podem manter as alterações da lista dentro da classe e verificar suas regras antes de aceitá-las.
Principais conclusões
- Uma invariante é verdadeira após a construção e após cada operação pública.
- Ignorar, limitar ao intervalo e lançar uma exceção são respostas diferentes a uma entrada inválida.
- Todo construtor e método que altera o estado deve preservar as mesmas regras.
- Reutilize a validação para que diferentes formas de alteração não possam contorná-la.
- Retornar uma lista interna mutável permite que o código externo contorne as verificações da classe.
- Uma cópia de uma lista interna de cadeias de caracteres protege a lista original de alterações externas na lista.
5Objetos legíveis e classes completas
Descrições legíveis de objetos facilitam o uso e a verificação de uma classe. Um método toString fornece esse texto sempre que um objeto é exibido ou concatenado a uma cadeia de caracteres. Você escreverá o método com o cabeçalho correto e depois reunirá campos, construtores, métodos e validação em uma classe completa. A partir de uma breve descrição, você transformará requisitos em código e testará chamadas comuns, ações repetidas e casos de valor-limite.
5.1 Uma descrição de objeto retornada como texto
Um objeto útil deve ser fácil de inspecionar como texto. O toString() herdado do Java retorna um nome de classe, um sinal @ e um código de dispersão hexadecimal, como Pet@8bcc55f. Um pacote nomeado passa a fazer parte do nome da classe. O texto do código de dispersão varia e geralmente diz pouco sobre o estado do objeto.
Retorne uma descrição legível
@Override
public String toString() {
return this.name + " (" + this.species + ", "
+ this.age + " years)";
}
Para Luna, uma gata de 3 anos, a cadeia de caracteres retornada é "Luna (cat, 3 years)". O método lê campos e constrói uma cadeia de caracteres. Ele não altera o animal nem envia saída à tela.
@Override pede ao compilador que verifique se a declaração realmente substitui um método existente. Isso detecta erros como escrever tostring com s minúsculo. A anotação não formata o resultado para você; a expressão de retorno determina o texto.
Reconheça chamadas automáticas
System.out.println(luna);
String label = "Pet: " + luna;
String details = luna.toString();
A primeira linha exibe a descrição legível do animal. A segunda constrói "Pet: Luna (cat, 3 years)". A terceira solicita a mesma descrição explicitamente e a armazena sem exibi-la. Quando um ArrayList de animais é exibido, sua representação usa o toString() de cada animal dentro dos colchetes da lista.
A conversão automática explica por que retornar texto é importante. O código que chama o método pode estar exibindo, armazenando ou concatenando o resultado. Um método que apenas exibe não pode fornecer a cadeia de caracteres necessária a todos esses códigos.
Use o cabeçalho correto do método
| Declaração | O que acontece |
|---|---|
public String toString() | Fornece a descrição do objeto |
public String tostring() | Declara um método diferente; @Override detecta o erro |
public String toString(int width) | Declara uma sobrecarga; a exibição automática não a chama |
public void toString() | Falha: entra em conflito com o método existente que retorna String |
Use o cabeçalho exato sem argumentos e inclua a anotação. Uma sobrecarga com parâmetros pode ser um método separado válido, mas a exibição de um objeto não fornece esses parâmetros.
Formate valores sem alterá-los
Um relógio precisa de horas e minutos com dois algarismos. Dentro de seu método toString():
return String.format(java.util.Locale.US,
"%02d:%02d", this.hours, this.minutes);
Para horas 9 e minutos 5, a cadeia de caracteres é "09:05". Cada %02d formata um inteiro em pelo menos duas posições, usando um zero à esquerda quando necessário. String.format(java.util.Locale.US, "%.2f", 7.5) retorna "7.50". Fornecer a configuração regional torna a saída com ponto decimal independente da configuração regional padrão de formatação do computador.
Mantenha a descrição curta o suficiente para ser lida durante a depuração. Ela pode revelar um estado não inicializado: concatenar um campo de nome null a uma cadeia de caracteres produz o texto "null", o que aponta para a inicialização. A descrição deve informar o estado atual, portanto exibir o objeto duas vezes não deve avançar um relógio nem celebrar um aniversário.
Principais conclusões
- Escreva public String toString() sem parâmetros e retorne texto legível.
- A anotação @Override permite que o compilador verifique se o método substitui um método existente.
- Exibir um objeto e concatená-lo a uma cadeia de caracteres usam toString automaticamente.
- Mantenha toString sem alterações de estado e deixe que o código que o chama decida para onde vai o texto.
- String.format pode preencher valores e usar uma configuração regional explícita para uma saída previsível.
5.2 De um requisito a uma classe testada
Comece pelo que um objeto deve lembrar, pelo que os códigos que o usam podem pedir que ele faça e por quais estados devem ser permitidos. Depois transforme cada parte em campos, construtores e métodos.
Extraia o projeto de uma descrição
“Um livro de biblioteca tem título, autor e número de páginas. Ele pode ser emprestado e devolvido. Ele não pode ser emprestado novamente enquanto já estiver emprestado.”
Use LibraryBook como nome da classe. Armazene os três valores descritivos e um estado de empréstimo booleano. O título, o autor e o número de páginas permanecem fixos, portanto declare-os final. O estado de empréstimo muda por meio de ações com funções claras. Um método modificador para cada campo é desnecessário.
Implemente a classe completa
public class LibraryBook {
private final String title;
private final String author;
private final int pages;
private boolean lentOut;
public LibraryBook(String title, String author, int pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.lentOut = false;
}
public boolean isLentOut() {
return this.lentOut;
}
public boolean lend() {
if (this.lentOut) {
return false;
}
this.lentOut = true;
return true;
}
public void giveBack() {
this.lentOut = false;
}
@Override
public String toString() {
String status = this.lentOut ? "lent out" : "available";
return this.title + " by " + this.author + ", "
+ this.pages + " pages, " + status;
}
}
O construtor define o livro como disponível. lend() verifica o estado existente antes de alterá-lo; retornar false em um segundo empréstimo preserva a regra. giveBack() torna o livro disponível. O método de acesso informa o estado, e toString() descreve o livro inteiro sem alterá-lo.
Anote os resultados esperados antes de executar as chamadas
No método main clássico de uma classe Main separada:
LibraryBook b = new LibraryBook("Night Train", "A. Rivera", 320);
System.out.println(b); // Night Train by A. Rivera, 320 pages, available
System.out.println(b.lend()); // true
System.out.println(b.lend()); // false
b.giveBack();
System.out.println(b.lend()); // true
A segunda tentativa de empréstimo é essencial: um método que sempre define a variável sinalizadora e retorna true passaria na primeira chamada, mas falharia nessa verificação. Teste o estado e também os valores de retorno com isLentOut(). Experimente também um segundo livro para verificar se um empréstimo de um livro deixa o outro disponível.
Teste onde o comportamento muda
Cubra cada construtor e método público. Para verificações de intervalo, experimente os dois extremos e valores imediatamente fora deles. Para um relógio, verifique a virada dos minutos e a passagem pela meia-noite. Para um método modificador que rejeita ou ignora entradas inválidas, verifique se elas têm o efeito prometido sobre o estado anterior. Escreva primeiro o resultado esperado para que uma saída inesperada não se torne, sem perceber, sua nova expectativa.
Implemente em pequenos passos: primeiro os campos e o construtor, depois uma descrição legível e, em seguida, uma operação e suas verificações por vez. Salve classes públicas comuns em arquivos com nomes correspondentes, compile-as juntas e execute a classe que contém main. Manter as operações do modelo separadas da saída na tela permite que a mesma classe sirva a diferentes programas.
Principais conclusões
- Transforme os dados que devem ser lembrados em campos privados e as ações permitidas em métodos.
- Escreva as regras de estado antes de implementar construtores e alterações.
- Use final para valores fixos de cada objeto e evite métodos modificadores desnecessários.
- Teste os valores de retorno e o estado resultante, incluindo chamadas repetidas e objetos separados.
- Verifique valores-limite e entradas inválidas, além de valores comuns.
- Implemente e teste uma parte útil por vez.
- Mantenha o comportamento do modelo separado do programa com main que exibe os resultados.
Exercícios resolvidos de Classes e objetos em Java
1De uma classe a dois animais
10 passos
- 1IntroduçãoUm tipo para as coisas no seu programa
Um programa sobre animais de estimação precisa lembrar nome, espécie e idade juntos. Uma classe dá um tipo a esse grupo e oferece métodos que o usam. Cada objeto é um animal criado a partir dessa definição.
Acompanhe a diferença entre a definição e os animais que ela produz. Depois rastreie dois objetos de animais e veja por que uma variável extra nem sempre significa um objeto extra.
- 2AnalogiaUma planta e suas instâncias
Pense em uma classe como uma planta de construção e em seus objetos como casas construídas a partir dela. A planta descreve o que cada casa contém, mas não é uma casa.
Uma definição de
Petpode descrever muitos animais. Cada um tem campos para os mesmos tipos de dados, mas um pode ser um gato de três anos e outro, um cachorro de cinco anos. Definir os campos cria a planta; criar objetos fornece instâncias reais. - 3ExemploIdentifique estado e comportamento
Os campos
name,specieseagede um animal armazenam seu estado. Para Luna, seus valores são"Luna","cat"e3.O método
celebrateBirthday()fornece comportamento. Seu corpo pode executarthis.age++;, alterando a idade do animal receptor. O método faz parte da definição da classe; o valor que ele altera pertence a um animal específico. - 4DesafioUma definição cria um animal?
Um programador escreve uma classe
Petcom três campos e um método de aniversário. Nenhuma expressão de criação de objeto foi executada. Quantos objetos de animais essa definição criou?---reveal---
Nenhum. A classe define um tipo e seu estado e comportamento disponíveis. Os objetos aparecem quando o programa os cria, por exemplo com
new Pet(...). Três declarações de campos descrevem três dados por animal, não três animais. - 5ExemploCrie o primeiro objeto
Leia
Pet luna = new Pet("Luna", "cat", 3);por partes.Pet lunadeclara uma variável.new Pet(...)cria um animal e executa o construtor com os três argumentos fornecidos.O construtor armazena os valores iniciais. A referência produzida por
newé atribuída aluna, dando ao código posterior uma forma de acessar esse objeto. Declarar apenasPet luna;não forneceria um objeto no qual chamar um método. - 6ExplicaçãoChame um método em seu objeto receptor
Em
luna.celebrateBirthday(), a expressão antes do ponto identifica o objeto receptor. O método de aniversário é executado para Luna, aumentando sua idade de 3 para 4.int age = luna.getAge();então obtém a idade atual para uso em outro lugar.Pet.celebrateBirthday()não pode escolher um animal: esse é um método de instância, e um nome de classe não fornece um objeto específico. - 7ExemploCrie um segundo animal
Agora execute
Pet max = new Pet("Max", "dog", 5);. Uma segunda expressãonewcria um segundo objeto com seus próprios campos.Após outra chamada a
luna.celebrateBirthday(), Luna tem 5 anos e Max continua com 5. Suas idades iguais não fazem deles o mesmo objeto. Um aniversário posterior de Luna altera apenas a idade dela porque a idade de Max fica nos campos de outro objeto. - 8Ideia-chaveUma atribuição pode compartilhar um objeto
Pet other = luna;copia a referência de Luna. Ela não chama um construtor nem copia seus campos para outro animal.Os nomes
otherelunaagora acessam o mesmo objeto. Chamarother.celebrateBirthday()altera a idade desse animal de 5 para 6, eluna.getAge()também vê 6. Conte as criações de objetos separadamente das declarações de variáveis. - 9DesafioRastreie uma referência compartilhada e um animal separado
Dois animais distintos começam com idades 1 para
ae 8 parab. O código executaPet c = a; c.celebrateBirthday(); b.celebrateBirthday();. Quais idadesaebinformam?---reveal---
Eles informam 2 e 9.
ccompartilha o objeto dea, portanto sua chamada aumenta a idade desse animal. A chamada por meio debaumenta a idade do animal separado. A classe fornece o mesmo comportamento de aniversário a ambos, mas cada chamada tem seu próprio objeto receptor. - 10RecapitulaçãoLeia a criação e as chamadas com atenção
Mantenha três papéis separados: a classe define o tipo, cada objeto armazena seu próprio estado de instância, e uma variável de referência acessa um objeto.
Ao ler o código, marque cada
new, confira seus argumentos em relação ao construtor e identifique o objeto receptor antes de rastrear uma chamada de método. Uma atribuição simples de referência adiciona um caminho para um objeto existente. Um segundonewcria um objeto separado com seu próprio estado.
2Construa o estado inicial corretamente
14 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoDê dados úteis a cada novo objeto
- 2ExplicaçãoEscolha o estado duradouro
- 3ExplicaçãoSepare valores padrão de valores úteis
- 4DesafioEscolha um campo ou uma variável local
- 5ExplicaçãoLeia o cabeçalho do construtor
- 6ExemploTransfira parâmetros para campos
- 7DicaUma atribuição válida que não armazena nada
- 8DesafioIdentifique o campo inalterado
- 9ConexãoCrie objetos internos também
- 10ExemploOfereça uma forma útil de criação com valores padrão
- 11ExplicaçãoAcompanhe um objeto pela cadeia
- 12DesafioIdentifique o problema em uma expressão new enganosa
- 13DesafioCombine um campo fixo com uma sobrecarga
- 14RecapitulaçãoVerifique cada caminho até um objeto concluído
3Rastreie alterações de estado e estado compartilhado
13 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhe o que cada chamada altera
- 2ExplicaçãoRetorne um valor ao código que chamou o método
- 3ExemploAltere a escala das dimensões armazenadas
- 4DesafioVerifique o resultado retornado após uma alteração
- 5ExemploAvance pela meia-noite
- 6DicaCorrija uma reinicialização que não altera o estado
- 7Ideia-chaveConsidere todos os retornos normais
- 8ExplicaçãoSepare duas contagens de um contador compartilhado
- 9ExemploForneça um objeto em código estático
- 10ConexãoReconheça uma constante
- 11DesafioRastreie os dois tipos de estado
- 12DesafioDescubra por que objetos separados mudam juntos
- 13RecapitulaçãoRelacione cada operação a quem a possui
4Proteja todos os caminhos de alteração do estado
14 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoTorne a classe responsável por suas regras
- 2ExplicaçãoOculte o campo por trás de uma operação
- 3DicaEscolha o acesso mais restrito que seja útil
- 4DesafioDecida onde o acesso privado funciona
- 5ExemploRetorne informações com um método de acesso
- 6ConexãoOfereça ações adequadas ao objeto
- 7DesafioLeia sem permitir substituição
- 8ExplicaçãoEscolha uma política para entradas inválidas
- 9ExemploRastreie a limitação no limite superior
- 10ExemploVerifique o estado inicial
- 11DesafioRastreie a opção de ignorar em vez de limitar
- 12Ideia-chaveUma lista privada ainda pode ser exposta
- 13DesafioFeche os dois caminhos sem verificação
- 14RecapitulaçãoExamine a interface pública
5Implemente e teste uma classe útil
12 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoTransforme um requisito em comportamento visível
- 2ExplicaçãoUse o cabeçalho exato da descrição
- 3ExemploRetorne o estado atual como texto
- 4DicaFormate um relógio sem alterá-lo
- 5DesafioCorrija a exibição automática
- 6ExemploEscolha campos a partir do requisito
- 7ExemploEstabeleça o estado inicial
- 8ExemploImplemente a regra antes da alteração
- 9DesafioPreveja uma sequência antes de executá-la
- 10ConexãoTeste os casos que uma execução simples não encontra
- 11DesafioDistinga informar de alterar
- 12RecapitulaçãoImplemente uma garantia pública testável
Questões de Classes e objetos em Java
Conjunto 1: Crie objetos e leia seu estado10 Questões de Prática
Questão 1 de 10
Ticket() { this(4); } e Ticket(int zones) { this.zones = zones + 1; }. Qual valor new Ticket() armazena em zones?Mostrar respostaOcultar resposta
Questão 2 de 10
new Badge("Visitor"), getText() retorna null. O campo é private String text;, o método de acesso o retorna e o corpo do construtor é text = text;. Como o construtor deve armazenar seu parâmetro text?Mostrar respostaOcultar resposta
this.text = text; escreve a entrada no objeto atual. Inverter a atribuição lê o campo ainda nulo para o parâmetro, e atribuir o campo a si mesmo também o deixa sem valor definido.Questão 3 de 10
Parcel para criar vários pacotes. Como é chamado um pacote criado durante a execução do programa?Mostrar respostaOcultar resposta
Parcel. A definição da classe descreve os dados e as operações de todos esses objetos; ela não é outro nome para um pacote específico. Um método é uma operação que o objeto pode executar.Questão 4 de 10
Bell está armazenado em bell, e ring() é um método de instância. Como o programa deve tocar essa campainha?Mostrar respostaOcultar resposta
ring() no objeto acessado por bell. A variável antes do ponto fornece o objeto receptor de que um método de instância precisa. Bell.ring() nomeia a classe, portanto não pode selecionar essa campainha para uma chamada de instância.Questão 5 de 10
private int attempts; sem inicializador nem atribuição no construtor. Qual valor um novo objeto armazena nesse campo?Mostrar respostaOcultar resposta
int recebe o valor padrão 0 antes da inicialização explícita. A regra que exige atribuição antes da leitura de uma variável local não inicializada não se aplica a campos. null é o valor padrão para campos de tipos de classe, não para um campo int.Questão 6 de 10
MinuteClock armazena horas de 0–23 e minutos de 0–59. advance() adiciona um minuto, tratando a virada da hora e da meia-noite. isMidnight() verifica se o horário é 00:00. Começando em 23:57, execute advance(); advance(); advance(); if (isMidnight()) { advance(); } advance();. Qual é o horário final?Mostrar respostaOcultar resposta
Questão 7 de 10
Lamp deve lembrar seu próprio estado ligado ou desligado. Como esse estado deve ser declarado?Mostrar respostaOcultar resposta
Questão 8 de 10
Bin é escrito como int capacity; sem um modificador de acesso. Qual é seu acesso?Mostrar respostaOcultar resposta
private.Questão 9 de 10
Session tem private boolean active;. Os códigos que a usam precisam verificar esse estado, mas só podem alterá-lo por meio de start() e stop(). Qual método de acesso se ajusta a esse projeto?Mostrar respostaOcultar resposta
Questão 10 de 10
public String toString() { return "Ready"; }. Qual texto é produzido por System.out.println("State: " + object);?Mostrar respostaOcultar resposta
"Ready" se combinam em "State: Ready". Definir um toString() personalizado sem argumentos substitui a descrição herdada com classe e código de dispersão; o Java não acrescenta esse texto herdado como prefixo.Conjunto 2: Verifique a inicialização e as alterações controladas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Locker tem um campo occupied e um método release(). Qual parte dessa classe descreve comportamento?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Panel armazena largura e altura. Sua largura pode mudar, e getArea() deve sempre informar a área atual. Como a área deve ser representada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
public Sensor(int channel). Um código usa new Sensor(). Por que isso não compila?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
private int reading = 9;. Seu método é public void reset() { int reading = 0; }. O que um método de acesso que retorna this.reading informa após reset()?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
minutesSinceMidnight, enquanto seus métodos de acesso continuam retornando os mesmos valores. Por que os códigos que o usam podem manter suas chamadas existentes aos métodos de acesso?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
setValue(25) e depois setValue(8), quais são os valores armazenados após cada chamada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Group deve conter no máximo quatro nomes. addName aplica o limite, mas Group(ArrayList<String> names) armazena diretamente a lista fornecida e getNames() retorna a lista interna. Qual projeto preserva o limite contra alterações feitas pelos códigos que usam a classe?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
toString() sem argumentos deve usar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
use() retorna false se ele já tiver sido usado; caso contrário, define a variável sinalizadora e retorna true. reset() limpa a variável sinalizadora. toString() apenas informa seu valor. O que as chamadas a use retornam em use(); println(pass); use(); reset(); use(); use();?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Acompanhe o estado dos objetos em sequências mais longas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Marker tem um nome e setName. Execute Marker a = new Marker("One"); Marker b = a; a = new Marker("Two"); b.setName("Three");. O que a.getName() e b.getName() retornam?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
private int level = 6;. Seu construtor é public Beacon(int level) { this.level = level + 2; }. O que new Beacon(3) armazena?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
public Tag(String text). Qual construtor adicional fornece uma sobrecarga distinta?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
scale(k) multiplica ambas as dimensões por k; area() retorna seu produto. Execute scale(2); double saved = area(); scale(0.5);. Quais são os valores de saved e do area() final?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Dial recém-criada tem o campo de instância value e o campo estático created, ambos inicialmente 0. Cada construtor incrementa created uma vez; turn() incrementa apenas o valor de seu objeto receptor. Execute Dial a = new Dial(); Dial b = a; b.turn(); b = new Dial(); b.turn(); b.turn();. Quais são os valores de a.value, b.value e created?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
private String label;. Qual implementação fornece esse valor ao código que o chama?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
setLevel. O construtor chama esse método modificador, e raise() chama setLevel(this.level + 1). Começando com new Level(13), execute raise(); setLevel(-2); raise(); raise();. Qual é o nível final?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
String.format(java.util.Locale.US, "%02d:%02d", 4, 7) retorna?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Seat deve recusar uma segunda reserva até que a primeira seja cancelada. Um teste verifica atualmente apenas a primeira chamada bem-sucedida a reserve(). Qual próxima chamada verifica melhor a regra de recusa?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Analise projetos e combine as regras10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Pump deve representar isso?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Token tem um valor numérico e addOne() o aumenta. Execute Token a = new Token(2); Token b = a; b.addOne(); Token c = new Token(7); a = c; a.addOne(); b.addOne();. Quais valores a, b e c informam?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
add(int amount) atualmente declara int total = amount; dentro do método. Qual alteração no armazenamento torna possível a acumulação?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Um Bucket declara private int capacity = 2; e uma lista de instância vazia items. Seu construtor é Bucket(int capacity) { capacity = capacity; }. add retorna false sem adicionar quando o tamanho da lista é pelo menos igual à capacidade armazenada; caso contrário, adiciona o item e retorna true. snapshot() retorna new ArrayList<>(this.items).
Execute Bucket b = new Bucket(4); b.add("A"); ArrayList<String> view = b.snapshot(); view.add("B");. Depois chame b.add("C") e b.add("D"). O que essas duas últimas chamadas retornam e quantos itens b contém ao final?
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Route tem o campo final code e o campo estático created, inicialmente 0. Seus construtores são Route() { this("local"); created++; } e Route(String code) { this.code = code; created++; }. Após uma chamada a cada construtor, created deve contar dois objetos. Qual correção produz a contagem certa?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
advance() adiciona um minuto com virada em 24 horas. getHour() lê a hora atual. Execute int h = getHour(); advance(); if (getHour() != h) { advance(); } advance();. Quais são h e o horário final?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
scale. Seu método de acesso retorna this.width * this.height, e não há campo de área. Por que esse é um projeto válido para um método de acesso?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Album tem um limite de três legendas. Seu construtor rejeita mais de três legendas e armazena new ArrayList<>(captions). getCaptions() retorna outra cópia; addCaption rejeita adições quando o limite é atingido. Um código fornece duas legendas, adiciona uma terceira à lista original, adiciona uma legenda ao resultado de um método de acesso e depois chama addCaption uma vez. Quantas legendas o Album contém?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Door deve recusar open() quando estiver trancada. Uma porta nova começa fechada e destrancada. lock() só é bem-sucedido enquanto ela está fechada; unlock() remove a trava. Qual sequência de teste verifica a recusa enquanto a porta está trancada e depois uma abertura bem-sucedida após destrancá-la?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Acesso de pacote
int count; permite que outra classe no mesmo pacote sem nome leia ou altere o campo.Anotação @Override
@Override pede ao compilador que verifique se uma declaração de método substitui um método já fornecido por um supertipo.@Override acima de tostring() revela o erro de letras maiúsculas e minúsculas em vez de deixar a exibição automática inalterada.Argumento
new Clock(9, 5), os argumentos 9 e 5 fornecem a hora e o minuto para o novo relógio.C
Campo estático
created pode aumentar cada vez que o construtor inicializa uma nova instância da classe.Campo final
Classe
Pet reúne nome, espécie e idade com operações como celebrateBirthday().Comportamento
lend() e giveBack() para que os códigos que o usam possam gerenciar seu estado de empréstimo.Constante
static final, muitas vezes público e nomeado em UPPER_SNAKE_CASE.MAX_CELSIUS fornece o mesmo limite superior de temperatura à classe e aos códigos que a usam.Construtor
Pet copia o nome, a espécie e a idade fornecidos para os campos do novo animal.Construtor padrão
new Pet() precisa de uma sobrecarga explícita.Construtor sem argumentos
Clock() pode delegar com this(0, 0) para iniciar o relógio à meia-noite.Contexto estático
pet.getAge() fornece um objeto específico.Cópia defensiva
new ArrayList<>(this.tracks) impede que alterações externas na lista afetem a lista interna.D
Desenvolvimento incremental
E
Encadeamento de construtores
this(...).this(name, species, 0) para reutilizar a inicialização com uma idade padrão.Encapsulamento
Pet combina um campo privado de idade com métodos de verificação que recusam alterações inválidas.Escopo
if termina nesse bloco, portanto o código posterior não pode usar seu nome.Estado
Exceção IllegalArgumentException
IllegalArgumentException é uma exceção que um método ou construtor pode lançar para rejeitar um argumento que viola suas regras de entrada.IllegalArgumentException para uma capacidade negativa impede que a construção retorne um objeto inválido.I
Inicialização
new ArrayList<>() para que entradas possam ser adicionadas posteriormente.Instância
Pet quando cada um é criado separadamente.Interface pública
lend(), giveBack() e isLentOut() enquanto sua variável sinalizadora de empréstimo permanece privada.Invariante
Invocação recursiva de construtor
Box() e Box(int) delegam um ao outro.L
Limitação ao intervalo (clamping)
M
Método de ação
lend() verifica o estado atual de empréstimo, rejeita um empréstimo repetido e informa se o empréstimo foi bem-sucedido.Método de acesso (getter)
get.getArea() pode calcular largura vezes altura mesmo quando a classe não armazena um campo de área separado.Método de instância
scale é um método de instância: a.scale(2) altera o retângulo a enquanto um retângulo separado permanece inalterado.Método estático
main pode criar Counter c e depois chamar c.increment() por meio dessa referência.Método modificador (setter)
setAge(int age) pode ignorar entradas negativas para que a idade armazenada permaneça no intervalo permitido.Método String.format
String.format constrói texto a partir de uma cadeia de formatação e valores, permitindo larguras e casas decimais fixas.String.format com java.util.Locale.US e "%.2f" formata 7,5 como "7.50", independentemente da configuração regional padrão.Método toString
toString() retorna a representação textual de um objeto, permitindo que a exibição e a concatenação de cadeias de caracteres mostrem detalhes legíveis.toString de um relógio retorna "09:05", portanto exibir o relógio mostra um horário útil em vez de um texto com classe e código de dispersão.Modificador de acesso
private, protected ou public, que controla o acesso a uma declaração.private mantém um método auxiliar disponível dentro de sua classe sem expô-lo a outras classes.Modificador private
private restringe o acesso a um membro ao código dentro de sua classe, mantendo os códigos de classes de nível superior separadas atrás da interface pública.pet.age = -1; falhar em uma classe Main separada, exigindo uma chamada de método controlada.Modificador protected
protected permite acesso pelo mesmo pacote e acesso restrito por código de subclasses fora desse pacote.Modificador public
public permite o acesso a um membro onde quer que o tipo que o declara seja acessível, para que outros códigos possam usar a operação exposta.lend() permite que Main solicite um empréstimo enquanto o campo privado de estado do livro permanece oculto.N
Notação de ponto
luna.getAge() informa ao Java que o animal acessado por luna deve informar sua idade.null
null significa que uma referência não tem objeto para acessar; campos de tipos de classe o recebem, a menos que sejam inicializados de outra forma.add em um campo de lista que ainda está null falha porque nenhum objeto de lista foi criado.O
Objeto
new separadas podem ter idades diferentes enquanto seguem uma mesma definição de classe.Objeto receptor
book.lend(), o objeto receptor é o livro acessado por book, cuja variável sinalizadora de empréstimo o método verifica.Ocultação de informações
getHours() consistente para os códigos existentes que o chamam.Operador new
new inicia uma expressão de criação de objeto que executa um construtor e produz uma referência ao objeto.new Pet("Luna", "cat", 3) cria um animal e fornece três argumentos para seu estado inicial.P
Palavra-chave this
this refere-se ao objeto atual dentro de código de instância, distinguindo seus campos de parâmetros com o mesmo nome.this.name = name; escreve a nova entrada no campo desse animal específico.Parâmetro
age em um construtor de animal recebe 3 quando a chamada fornece 3 nessa posição.Projeto de classes
Propriedade somente de leitura
getTitle(), mas nenhuma operação para substituí-lo.R
Rastreamento de estado
Referência
Pet other = luna;, as duas variáveis armazenam referências ao mesmo animal, portanto qualquer uma delas pode acessá-lo.Resultado esperado
lend() em um livro emprestado é false, com a variável sinalizadora de empréstimo inalterada.S
Sobrecarga de construtores
Sombreamento
this.age = age; útil: o lado esquerdo é o campo, e o direito é a entrada.T
Tempo de vida
Teste de valor-limite
V
Validação
Valor de retorno
r.area() pode ser dobrado em uma expressão; apenas exibir a área não fornece um valor.Valor padrão
false; um booleano local deve receber um valor antes de poder ser lido.Variável de instância (campo de instância)
age de cada animal mantém seu próprio valor, embora a declaração apareça apenas uma vez em Pet.Variável local
total dentro de um método de cálculo armazena trabalho temporário que o objeto não precisa lembrar.void
void declara que um método não fornece valor de retorno, mesmo quando o método altera o estado ou exibe uma saída.void porque sua função é aumentar a idade armazenada de seu objeto receptor.- 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.