Versão para Impressão
Versão para Impressão
Tips
Antes de criar o universo, o Deus precisa decidir como vai criá-lo.
📖 A Revelação
O que é um paradigma?
A palavra paradigma vem do grego parádeigma e significa modelo, padrão, exemplo a ser seguido. No dicionário Aurélio:
Paradigma
Algo que serve de exemplo geral ou de modelo.
Na programação, um paradigma é a forma como você organiza o pensamento para resolver problemas com código. É como se fosse a filosofia por trás da linguagem. Duas linguagens podem resolver o mesmo problema de formas completamente diferentes — tudo depende do paradigma que seguem.
Pense assim: antes de construir uma casa, você precisa decidir como vai construir. Com tijolos? Com madeira? Com impressão 3D? Cada abordagem tem suas regras, vantagens e limitações. O mesmo acontece com código.
Os principais paradigmas
| Paradigma | Ideia Central | Exemplo de Linguagem |
|---|---|---|
| Imperativo / Estruturado | Sequência de instruções que mudam o estado do programa | C, Pascal |
| Orientado a Objetos | Organização em objetos que possuem dados e comportamentos | Java, C#, Python |
| Funcional | Avaliação de funções matemáticas puras | Haskell, Elixir |
| Lógico | Regras e inferências lógicas | Prolog |
| Declarativo | Descreve o que fazer, não como | SQL, HTML |
Multiparadigma
Muitas linguagens modernas são multiparadigma: Java, Python, C#, JavaScript. Elas permitem usar mais de um estilo. Mas cada uma tem um paradigma dominante — e o de Java é a Orientação a Objetos.
Nesta disciplina, nosso foco será o orientado a objetos. Entender a diferença entre ele e o estruturado é o primeiro passo para se tornar um Deus Criador de universos digitais.
A Gênese
O Caos Primordial: A Programação Estruturada

Imagine que, antes de existirem universos, havia apenas o Caos. Um vazio onde todas as coisas existiam soltas, desorganizadas, sem forma.
Na programação estruturada, é mais ou menos assim: você tem dados de um lado e funções de outro. Os dados ficam expostos, vulneráveis, e qualquer função pode mexer neles. Não existe dono. Não existe proteção. Não existe identidade.
É como um universo onde os átomos flutuam sem lei, sem gravidade, sem ordem. Funciona? Até funciona — para universos pequenos. Mas quando o universo cresce... o caos vence.
Warning
Na programação estruturada, os dados são como órgãos espalhados pelo chão. Qualquer um pode mexer no coração, no fígado, no cérebro. Não existe corpo. Não existe criatura. Só peças soltas.
A Ordem Divina: A Programação Orientada a Objetos
Agora imagine que um Deus Criador resolve colocar ordem no caos. Ele decide:
- Cada criatura terá seu próprio corpo (dados e comportamentos juntos)
- Cada criatura será única (cada objeto é independente)
- Ninguém poderá mexer nas entranhas de uma criatura sem permissão (encapsulamento)
- As criaturas poderão ter linhagens (herança)
- Criaturas poderão assinar pactos para ganhar poderes (interfaces)
Na orientação a objetos, dados e comportamentos vivem juntos, dentro de uma mesma estrutura chamada classe. Cada exemplar gerado a partir dessa classe é um objeto — uma criatura viva no universo digital.
Warning
A Orientação a Objetos não é apenas uma forma de programar. É uma forma de pensar. Você deixa de dar ordens a um computador e passa a criar seres que interagem entre si.
💻 O Código Sagrado
Vamos ver a diferença na prática. Imagine um sistema simples: gerenciar criaturas com nome e vida.
🔴 No Paradigma Estruturado (em C)
Dados e funções existem separados. Qualquer função pode acessar e modificar qualquer dado diretamente.
#include <stdio.h>
#include <string.h>
// Dados — soltos, expostos
struct Criatura {
char nome[50];
int vida;
};
// Funções — separadas dos dados
void receberDano(struct Criatura *c, int dano) {
c->vida -= dano;
if (c->vida < 0) c->vida = 0;
}
void exibirStatus(struct Criatura *c) {
printf("Nome: %s | Vida: %d\n", c->nome, c->vida);
}
int main() {
struct Criatura fenix;
strcpy(fenix.nome, "Fenix");
fenix.vida = 100;
exibirStatus(&fenix);
receberDano(&fenix, 30);
exibirStatus(&fenix);
// ⚠️ Qualquer um pode fazer isso:
fenix.vida = 999999; // Ninguém impediu!
exibirStatus(&fenix);
return 0;
}O que há de errado?
- Os dados da criatura estão expostos. Qualquer parte do código pode alterar
vidadiretamente. - As funções
receberDanoeexibirStatusnão pertencem à criatura. Elas existem soltas no universo. - Se criarmos outros tipos de criaturas (Dragão, Guerreiro), teremos que criar novas funções para cada tipo ou encher as funções de
if-else. - À medida que o sistema cresce, o código vira um emaranhado de funções e
structssem relação clara.
🟢 No Paradigma Orientado a Objetos (em Java)
Dados e comportamentos vivem juntos dentro do objeto.
// O Molde — a Forma da Criação
public class Criatura {
// A Essência (atributos) — protegida dentro do corpo
String nome;
int vida;
// Os Poderes (métodos) — pertencem à criatura
void receberDano(int dano) {
this.vida -= dano;
if (this.vida < 0) this.vida = 0;
}
void exibirStatus() {
IO.println("Nome: " + this.nome + " | Vida: " + this.vida);
}
}// O Deus Criador escreve as leis do universo
public class Universo {
public static void main(String[] args) {
// O Gesto da Criação — uma criatura nasce!
Criatura fenix = new Criatura();
fenix.nome = "Fenix";
fenix.vida = 100;
fenix.exibirStatus(); // A criatura responde!
fenix.receberDano(30); // A criatura sabe se proteger
fenix.exibirStatus();
// Criando outra criatura do mesmo molde
Criatura smaug = new Criatura();
smaug.nome = "Smaug";
smaug.vida = 200;
smaug.exibirStatus();
}
}O que mudou?
nomeevidaestão dentro da classeCriatura— eles pertencem a ela.receberDano()eexibirStatus()também estão dentro da classe — a criatura sabe o que fazer consigo mesma.- Cada criatura (
fenix,smaug) é um objeto independente, com sua própria essência. - O código do
mainnão precisa saber como a criatura recebe dano — só precisa pedir. Isso é o início do encapsulamento.
O Comparativo Visual
⚡ Resumo das Diferenças
| Aspecto | Estruturado | Orientado a Objetos |
|---|---|---|
| Organização | Dados + funções separados | Dados + métodos juntos (classe) |
| Unidade básica | Função / procedimento | Objeto |
| Dados | Expostos (qualquer um acessa) | Protegidos (encapsulamento) |
| Reutilização | Copiar e colar funções | Herança e composição |
| Complexidade | Funciona bem para problemas pequenos | Escala para sistemas grandes |
| Metáfora | Órgãos soltos no chão | Criaturas vivas com corpo e poderes |
Importante
A Programação Estruturada não é ruim — ela é excelente para scripts curtos, automações simples e problemas pequenos. Mas quando o sistema cresce, a Orientação a Objetos oferece ferramentas poderosas para organizar, proteger e reutilizar código.
Tips
No primeiro dia, o Deus decidiu que o Caos não bastava. Era hora de criar Moldes — e dar vida às Criaturas.
📖 A Revelação
O que é uma Classe?
Na aula anterior, você entendeu que na Programação Orientada a Objetos os dados e comportamentos vivem juntos. Agora é hora de entender onde eles vivem juntos: dentro de uma Classe.
Classe
Uma classe é a descrição de um conjunto de entidades que compartilham os mesmos atributos (características), métodos (comportamentos) e semântica (significado). É o molde a partir do qual objetos são criados.
A palavra classe vem da taxonomia da biologia. Todos os seres vivos de uma mesma classe biológica têm uma série de atributos e comportamentos em comum — mas não são iguais. Podem variar nos valores desses atributos e na forma como realizam esses comportamentos.
Pense na espécie Homo Sapiens. Ela define um grupo de seres com características em comum: possuem nome, idade, altura. Mas Homo Sapiens é um ser humano? Não. Homo Sapiens é a especificação. Para ter um ser humano de verdade, você precisa de uma instância — um objeto concreto criado a partir dessa especificação.
Analogias
- Uma receita de bolo não é um bolo. Você precisa instanciá-la para comer.
- A planta de uma casa não é uma casa. Você mora no objeto, não no molde.
- O molde de uma criatura não é uma criatura. Você precisa dar vida a ela.
O que é um Objeto?
Um objeto é uma instância de uma classe — uma entidade concreta, única e independente que existe no universo do programa. Cada objeto:
- É único — mesmo que dois objetos venham do mesmo molde, são entidades diferentes
- Possui atributos que definem suas características e seu estado atual
- Possui métodos que definem o que ele pode fazer
- É independente — armazena seus próprios dados e executa suas próprias ações
Exemplo
Objeto do tipo Conta
- Estrutura (Atributos): titular, número, saldo, limite
- Comportamento (Métodos): sacar, depositar, transferir
Atributos
Os atributos são as propriedades de uma classe. Eles descrevem as características que cada objeto terá. Após a classe ser instanciada em um objeto, os atributos recebem valores concretos que definem aquele objeto específico.
class Conta {
int numero; // atributo
String cliente; // atributo
double saldo; // atributo
double limite; // atributo
}Cada conta criada a partir desse molde terá seu próprio número, seu próprio cliente, seu próprio saldo. O molde é um só — as criaturas são muitas.
Métodos
Os métodos definem o que um objeto pode fazer. São as ações, os comportamentos, as habilidades da criatura.
- São acionados por outros objetos (ou pelo próprio programa)
- É assim que os objetos se comunicam — através de chamadas de métodos (troca de mensagens)
Um método pode:
- Não retornar nada (
void) — executa uma ação sem devolver informação - Retornar um valor — executa uma ação e devolve uma resposta
Construtor
O construtor é um método especial que é executado automaticamente no momento em que um objeto é criado. Ele serve para:
- Inicializar os atributos do objeto com valores válidos
- Garantir que toda criatura nasça com uma essência definida
- Evitar que objetos existam em estados inválidos ou vazios
Regras do construtor:
- Tem o mesmo nome da classe
- Não tem tipo de retorno (nem
void) - É chamado automaticamente pelo operador
new - Se você não criar nenhum, Java fornece um construtor padrão (sem parâmetros)
- Uma classe pode ter vários construtores com parâmetros diferentes
🌌 A Gênese
No Primeiro Dia, o Deus Criou os Moldes
Na aula anterior, o Deus Criador separou a Ordem do Caos. Ele entendeu que dados e comportamentos devem viver juntos, protegidos, dentro de uma mesma estrutura. Agora é hora de dar o próximo passo: criar os Moldes.

Um Molde (📐 Classe) é o projeto divino — a planta baixa de cada tipo de criatura que existirá no universo. Nele, o Deus define:
- A Essência (🧬 Atributos) — o que a criatura é: seu nome, sua vida, seu tipo
- Os Poderes (⚡ Métodos) — o que a criatura pode fazer: atacar, se curar, exibir seu status
- O Ritual de Nascimento (🌅 Construtor) — o momento exato em que a criatura ganha vida, com sua essência já definida
Warning
O Molde não é a criatura. Ele é a ideia da criatura. Para que uma criatura de verdade exista no universo, o Deus deve realizar o Gesto da Criação: invocar new e dar vida ao molde.
Cada criatura (🐾 Objeto) que nasce a partir de um molde é única. Duas criaturas podem vir do mesmo projeto, mas cada uma tem sua própria essência — seu próprio nome, sua própria vida.
Tips
O Molde é a ideia. A Criatura é a realidade. Sem o new, você tem um sonho. Com o new, você tem um ser vivo.
Mas um Deus sábio não cria criaturas sem regras. Ele define um Ritual de Nascimento (🌅 Construtor) — um procedimento que garante que toda criatura nasça com sua essência já definida. Sem esse ritual, as criaturas nasceriam vazias, sem nome, sem vida — fantasmas no universo digital.
"Uma classe sem construtor é como um universo sem Big Bang. Até existe… mas nada acontece."
💻 O Código Sagrado
Fase 1 — O Primeiro Molde (Classe com Atributos)
O Deus Criador começa simples. Ele projeta o primeiro molde — uma Criatura com essência básica:
// 📐 O Molde da Criação — a Forma de toda Criatura
public class Criatura {
// 🧬 A Essência — o que define cada criatura
String nome;
int vida;
String tipo;
}Três linhas. Três atributos. Três peças de essência que toda criatura carregará. Mas o molde sozinho não faz nada. É hora de criar uma criatura de verdade.
Fase 2 — O Gesto da Criação (Instanciando Objetos)

public class Universo {
public static void main(String[] args) {
// 🌅 O Gesto da Criação — uma criatura nasce!
Criatura fenix = new Criatura();
fenix.nome = "Fênix";
fenix.vida = 100;
fenix.tipo = "Ave de Fogo";
// Outra criatura do mesmo molde — mas única!
Criatura smaug = new Criatura();
smaug.nome = "Smaug";
smaug.vida = 200;
smaug.tipo = "Dragão";
IO.println(fenix.nome + " tem " + fenix.vida + " de vida.");
IO.println(smaug.nome + " tem " + smaug.vida + " de vida.");
}
}Observe: fenix e smaug vêm do mesmo molde, mas são criaturas diferentes. Cada uma tem sua própria essência.
Fase 3 — Os Poderes Concedidos (Métodos)
Um molde sem poderes cria criaturas inertes. O Deus agora concede habilidades:
Métodos sem retorno (void)
public class Criatura {
String nome;
int vida;
String tipo;
// ⚡ Poder: receber dano
void receberDano(int dano) {
vida -= dano;
if (vida < 0) vida = 0;
}
// ⚡ Poder: exibir seu status
void exibirStatus() {
IO.println(nome + " [" + tipo + "] - Vida: " + vida);
}
// ⚡ Poder: curar-se
void curar(int cura) {
vida += cura;
}
}Agora a criatura sabe o que fazer. Ela sabe receber dano, sabe se curar, sabe exibir seu status. O Deus não precisa mais manipular as entranhas diretamente — ele dá uma ordem, e a criatura age.
public class Universo {
public static void main(String[] args) {
Criatura fenix = new Criatura();
fenix.nome = "Fênix";
fenix.vida = 100;
fenix.tipo = "Ave de Fogo";
fenix.exibirStatus(); // Fênix [Ave de Fogo] - Vida: 100
fenix.receberDano(30); // A criatura sabe se proteger
fenix.exibirStatus(); // Fênix [Ave de Fogo] - Vida: 70
fenix.curar(10); // A criatura sabe se curar
fenix.exibirStatus(); // Fênix [Ave de Fogo] - Vida: 80
}
}Métodos com retorno
Às vezes, o Deus quer que a criatura responda quando é acionada. Para isso, o método deve retornar um valor:
public class Criatura {
String nome;
int vida;
String tipo;
// ⚡ Poder com resposta: sacar vida com validação
boolean receberDano(int dano) {
if (dano > vida) {
return false; // Dano excessivo — recusado
}
vida -= dano;
return true; // Dano aplicado com sucesso
}
// ⚡ Poder com resposta: verificar se está viva
boolean estaViva() {
return vida > 0;
}
void exibirStatus() {
IO.println(nome + " [" + tipo + "] - Vida: " + vida);
}
}public class Universo {
public static void main(String[] args) {
Criatura fenix = new Criatura();
fenix.nome = "Fênix";
fenix.vida = 100;
fenix.tipo = "Ave de Fogo";
boolean sobreviveu = fenix.receberDano(30);
if (sobreviveu) {
IO.println("A criatura resistiu ao golpe!");
} else {
IO.println("O golpe foi demais para a criatura!");
}
IO.println("Está viva? " + fenix.estaViva());
}
}Métodos que recebem objetos
Criaturas podem interagir entre si. O verdadeiro poder surge quando um objeto comunica-se com outro:
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
void atacar(Criatura alvo) {
IO.println(nome + " ataca " + alvo.nome + "!");
alvo.receberDano(forca);
}
void receberDano(int dano) {
vida -= dano;
if (vida < 0) vida = 0;
}
void exibirStatus() {
IO.println(nome + " [" + tipo + "] - Vida: " + vida);
}
}public class Universo {
public static void main(String[] args) {
Criatura fenix = new Criatura();
fenix.nome = "Fênix";
fenix.vida = 100;
fenix.tipo = "Ave de Fogo";
fenix.forca = 30;
Criatura smaug = new Criatura();
smaug.nome = "Smaug";
smaug.vida = 200;
smaug.tipo = "Dragão";
smaug.forca = 50;
fenix.exibirStatus();
smaug.exibirStatus();
fenix.atacar(smaug); // Fênix ataca Smaug!
smaug.atacar(fenix); // Smaug contra-ataca!
fenix.exibirStatus();
smaug.exibirStatus();
}
}Fase 4 — O Ritual de Nascimento (Construtor)
Até agora, nossas criaturas nascem vazias e precisamos preencher seus atributos um a um. Isso é perigoso — e se o Deus esquecer de definir o nome? A criatura existirá sem identidade!
O Construtor resolve isso. Ele é o Ritual de Nascimento — garante que toda criatura nasça com sua essência já definida:
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
// 🌅 O Ritual de Nascimento — toda criatura nasce completa
Criatura(String nome, int vida, String tipo, int forca) {
this.nome = nome;
this.vida = vida;
this.tipo = tipo;
this.forca = forca;
}
void atacar(Criatura alvo) {
IO.println(this.nome + " ataca " + alvo.nome + "!");
alvo.receberDano(this.forca);
}
void receberDano(int dano) {
this.vida -= dano;
if (this.vida < 0) this.vida = 0;
}
void exibirStatus() {
IO.println(this.nome + " [" + this.tipo + "] - Vida: " + this.vida);
}
boolean estaViva() {
return this.vida > 0;
}
}Agora, o Gesto da Criação é mais poderoso e seguro:
public class Universo {
public static void main(String[] args) {
// 🌅 Criaturas nascem completas — o Ritual garante!
Criatura fenix = new Criatura("Fênix", 100, "Ave de Fogo", 30);
Criatura smaug = new Criatura("Smaug", 200, "Dragão", 50);
Criatura arthas = new Criatura("Arthas", 150, "Guerreiro", 40);
fenix.exibirStatus();
smaug.exibirStatus();
arthas.exibirStatus();
IO.println("--- A Batalha Começa ---");
fenix.atacar(smaug);
smaug.atacar(arthas);
arthas.atacar(fenix);
fenix.exibirStatus();
smaug.exibirStatus();
arthas.exibirStatus();
}
}A palavra-chave this
Dentro de um método ou construtor, this se refere ao próprio objeto — à criatura que está executando a ação. Quando escrevemos this.nome = nome, estamos dizendo: "o atributo nome desta criatura recebe o valor do parâmetro nome".
Múltiplos Construtores
Uma classe pode ter vários Rituais de Nascimento — cada um para uma situação diferente:
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
// Ritual completo
Criatura(String nome, int vida, String tipo, int forca) {
this.nome = nome;
this.vida = vida;
this.tipo = tipo;
this.forca = forca;
}
// Ritual simplificado — criatura nasce com valores padrão
Criatura(String nome) {
this.nome = nome;
this.vida = 100; // vida padrão
this.tipo = "Desconhecido";
this.forca = 10; // força padrão
}
// ... métodos ...
}// Duas formas de criar:
Criatura fenix = new Criatura("Fênix", 100, "Ave de Fogo", 30); // Ritual completo
Criatura misterio = new Criatura("???"); // Ritual simplificadoResumo visual: anatomia de uma Classe
Tips
As criaturas já existem. Agora é hora de entender como elas vivem, se comunicam e se reconhecem no universo.
📖 A Revelação
Referência: onde a criatura realmente mora?
Na aula anterior, você aprendeu a criar classes com atributos, métodos e construtores. Mas quando escrevemos algo como:
Criatura fenix = new Criatura("Fênix", 100, "Ave de Fogo", 30);O que exatamente está guardado na variável fenix? A criatura inteira? Não.
Referência
Uma variável de tipo objeto não armazena o objeto em si. Ela armazena uma referência — o endereço de memória onde o objeto foi criado. É como uma placa com o endereço da casa, não a casa em si.
Isso tem consequências profundas:
- Atribuir uma variável a outra (
c2 = c1) não copia o objeto — apenas copia o endereço. Ambas passam a apontar para o mesmo objeto. - Comparar duas variáveis com
==compara os endereços, não o conteúdo dos objetos. - Passar um objeto como parâmetro de método envia a referência — o método pode alterar o objeto original.
Comunicação entre objetos
Os objetos não existem isolados. Eles interagem entre si através de mensagens — que, em Java, são chamadas de métodos.
Quando um objeto chama um método de outro, ele está enviando uma mensagem:
- O remetente é quem chama o método
- O destinatário é quem executa
- A mensagem é o nome do método + seus parâmetros
- A resposta é o valor de retorno (se houver)
É assim que o universo funciona: criaturas se comunicam, colaboram e interagem. Um objeto nunca faz tudo sozinho — o poder está na interação.
Comparação: == vs equals
Em Java, existem duas formas de comparar objetos — e entender a diferença é essencial:
| Operador / Método | O que compara | Quando usar |
|---|---|---|
== | Os endereços (referências) | Verificar se são o mesmo objeto |
equals() | O conteúdo (como você definir) | Verificar se são equivalentes |
Por padrão, o método equals() herdado de Object compara endereços (igual ao ==). Para comparar pelo conteúdo, você precisa sobrescrever o método na sua classe.
toString: a identidade da criatura
Quando você tenta imprimir um objeto com IO.println(fenix), o Java automaticamente chama o método toString() desse objeto. Por padrão, ele retorna algo como Criatura@1a2b3c — o nome da classe seguido do endereço de memória. Nada útil.
toString
O método toString() é herdado da classe Object e retorna uma representação em texto do objeto. Toda classe pode sobrescrever esse método para fornecer uma descrição significativa.
Sobrescrever o toString() permite que suas criaturas se apresentem de forma legível e informativa.
🌌 A Gênese
As Criaturas Aprendem a Se Reconhecer
O Deus Criador já sabe dar forma às criaturas. Já definiu moldes, essências, poderes e rituais de nascimento. Mas agora enfrenta um novo desafio: como as criaturas se relacionam no universo?

No mundo real, cada ser tem um corpo e um nome pelo qual é chamado. O nome não é o ser — é uma forma de apontar para ele. Se duas pessoas apontam para a mesma criatura, ambas estão falando do mesmo ser. Se cada uma aponta para uma criatura diferente, são seres distintos — mesmo que pareçam idênticos.
Na POO, funciona igual. A variável é o nome (a referência). O objeto é a criatura (na memória). E o new é o gesto da criação que faz a criatura existir.
"A variável não é a criatura. É o dedo que aponta para ela. Se dois dedos apontam para o mesmo lugar, há uma só criatura — não duas."
Mas o Deus percebeu outro problema: quando duas criaturas nascem do mesmo molde, com a mesma essência, como saber se são a mesma criatura ou apenas gêmeas? O operador == responde: "vocês moram no mesmo endereço?" — mas nem sempre é isso que queremos saber. Às vezes queremos perguntar: "vocês são equivalentes?"
Para isso, o Deus criou o poder equals() — uma habilidade que cada criatura pode personalizar para definir o que significa ser igual.
E por fim, o Deus quis que suas criaturas pudessem se apresentar de forma elegante. Sem o toString(), quando perguntamos "quem é você?", a criatura responde com seu endereço de memória — como se dissesse "moro na rua 0x7A3F". Com o toString(), ela pode responder "Sou Fênix, Ave de Fogo, com 100 de vida".
"Sem
toString(), a criatura é um fantasma sem voz. ComtoString(), ela se apresenta ao universo."
💻 O Código Sagrado
Nesta aula, usaremos a classe Criatura que construímos progressivamente na aula anterior, com construtor e métodos:
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
// 🌅 Ritual de Nascimento
Criatura(String nome, int vida, String tipo, int forca) {
this.nome = nome;
this.vida = vida;
this.tipo = tipo;
this.forca = forca;
}
void atacar(Criatura alvo) {
IO.println(this.nome + " ataca " + alvo.nome + "!");
alvo.receberDano(this.forca);
}
void receberDano(int dano) {
this.vida -= dano;
if (this.vida < 0) this.vida = 0;
}
void exibirStatus() {
IO.println(this.nome + " [" + this.tipo + "] - Vida: " + this.vida);
}
boolean estaViva() {
return this.vida > 0;
}
}Referências na prática
Duas referências, dois objetos
public class Universo {
public static void main(String[] args) {
Criatura c1 = new Criatura("Fênix", 100, "Ave de Fogo", 30);
Criatura c2 = new Criatura("Smaug", 200, "Dragão", 50);
}
}Aqui, c1 e c2 apontam para criaturas diferentes. Cada uma tem seu próprio espaço na memória, sua própria essência.
Duas referências, um único objeto
public class Universo {
public static void main(String[] args) {
Criatura c1 = new Criatura("Fênix", 100, "Ave de Fogo", 30);
Criatura c2 = c1; // c2 aponta para a MESMA criatura!
c2.nome = "Fênix Renascida";
IO.println(c1.nome); // Imprime "Fênix Renascida"!
}
}Cuidado!
c2 = c1 não criou uma nova criatura. Apenas fez c2 apontar para o mesmo objeto que c1. Alterar a criatura por c2 é o mesmo que alterar por c1 — porque é a mesma criatura.
Referência nula
Uma variável pode ser declarada sem apontar para nenhum objeto:
Criatura fantasma = null; // Nenhuma criatura existe aqui
fantasma.exibirStatus(); // 💥 ERRO! NullPointerException!Warning
null significa que a referência não aponta para ninguém. Tentar invocar um poder numa referência nula é como gritar ordens para o vazio — o universo responde com uma catástrofe (NullPointerException).
Comunicação entre objetos
Criaturas se comunicam chamando os métodos umas das outras. Vamos aprofundar o exemplo da transferência entre contas da classe Conta:
public class Conta {
int numero;
String cliente;
double saldo;
double limite;
Conta(int numero, String cliente) {
this.numero = numero;
this.cliente = cliente;
this.saldo = 0;
this.limite = 0;
}
void depositar(double valor) {
this.saldo += valor;
}
boolean sacar(double valor) {
if (this.saldo + this.limite >= valor) {
this.saldo -= valor;
return true;
}
return false;
}
// ⚡ O poder mais elegante: transferir reutiliza sacar e depositar
boolean transferir(Conta destino, double valor) {
if (this.sacar(valor)) {
destino.depositar(valor);
return true;
}
return false;
}
void exibirExtrato() {
IO.println("Conta " + this.numero + " | " + this.cliente
+ " | Saldo: " + this.saldo + " | Limite: " + this.limite);
}
}Observe o método transferir: ele recebe outra conta como parâmetro. Quando c1.transferir(c2, 200) é executado, a conta c1 saca de si mesma e deposita na conta c2. Dois objetos colaborando!
public class Universo {
public static void main(String[] args) {
Conta c1 = new Conta(1, "Leandro");
Conta c2 = new Conta(2, "Maria");
c1.depositar(1000);
c1.exibirExtrato(); // Saldo: 1000
c2.exibirExtrato(); // Saldo: 0
c1.transferir(c2, 200);
c1.exibirExtrato(); // Saldo: 800
c2.exibirExtrato(); // Saldo: 200
}
}"O método
transferirnão inventa nada novo. Ele compõe poderes que já existem:sacaredepositar. Isso é elegância — um Criador eficiente reutiliza, nunca repete."
Comparação de objetos
O operador == compara referências
public class Universo {
public static void main(String[] args) {
Criatura c1 = new Criatura("Fênix", 100, "Ave de Fogo", 30);
Criatura c2 = new Criatura("Fênix", 100, "Ave de Fogo", 30);
if (c1 == c2) {
IO.println("Mesma criatura!");
} else {
IO.println("Criaturas diferentes!"); // ✅ Este é o resultado!
}
}
}Mesmo com atributos idênticos, c1 == c2 retorna false. Por quê? Porque == compara os endereços de memória, e cada new cria um objeto em um endereço diferente. São gêmeas — não a mesma criatura.
Warning
O == pergunta: "vocês moram no mesmo lugar?" — não "vocês são iguais?". Para comparar conteúdo, precisamos do equals().
O método equals() compara conteúdo
Para que duas criaturas possam ser comparadas pelo seu conteúdo, precisamos sobrescrever o método equals():
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
Criatura(String nome, int vida, String tipo, int forca) {
this.nome = nome;
this.vida = vida;
this.tipo = tipo;
this.forca = forca;
}
// Definindo o critério de igualdade: criaturas com mesmo nome são iguais
public boolean equals(Criatura outra) {
return this.nome.equals(outra.nome);
}
// ... demais métodos ...
}Agora podemos comparar pelo conteúdo:
public class Universo {
public static void main(String[] args) {
Criatura c1 = new Criatura("Fênix", 100, "Ave de Fogo", 30);
Criatura c2 = new Criatura("Fênix", 150, "Ave de Gelo", 40);
IO.println(c1 == c2); // false — endereços diferentes
IO.println(c1.equals(c2)); // true — mesmo nome!
}
}O critério de igualdade é definido por você, o Deus Criador. No exemplo acima, decidimos que duas criaturas são iguais se tiverem o mesmo nome. Poderia ser pelo tipo, pela vida, ou por uma combinação de atributos — depende da regra do seu universo.
Outro exemplo, com a classe Conta:
public class Conta {
int numero;
String cliente;
double saldo;
double limite;
// ... construtores e métodos ...
// Duas contas são iguais se tiverem o mesmo número
public boolean equals(Conta outraConta) {
return this.numero == outraConta.numero;
}
}toString: dando voz à criatura
Sem toString() — a resposta padrão
Criatura fenix = new Criatura("Fênix", 100, "Ave de Fogo", 30);
IO.println(fenix); // Imprime algo como: Criatura@1a2b3c4dIsso acontece porque o toString() padrão (herdado de Object) retorna o nome da classe + endereço de memória. Nada informativo.
Com toString() — a criatura se apresenta
Sobrescreva o toString() para dar uma identidade legível à criatura:
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
Criatura(String nome, int vida, String tipo, int forca) {
this.nome = nome;
this.vida = vida;
this.tipo = tipo;
this.forca = forca;
}
// A criatura se apresenta ao universo
@Override
public String toString() {
return this.nome + " [" + this.tipo + "] - Vida: " + this.vida
+ " | Força: " + this.forca;
}
public boolean equals(Criatura outra) {
return this.nome.equals(outra.nome);
}
void atacar(Criatura alvo) {
IO.println(this.nome + " ataca " + alvo.nome + "!");
alvo.receberDano(this.forca);
}
void receberDano(int dano) {
this.vida -= dano;
if (this.vida < 0) this.vida = 0;
}
boolean estaViva() {
return this.vida > 0;
}
}Agora:
Criatura fenix = new Criatura("Fênix", 100, "Ave de Fogo", 30);
IO.println(fenix); // Fênix [Ave de Fogo] - Vida: 100 | Força: 30O IO.println() automaticamente invoca o toString() do objeto. A criatura agora sabe se apresentar.
Tips
O @Override indica que estamos sobrescrevendo um método herdado da classe Object. Falaremos mais sobre herança em aulas futuras — por enquanto, basta saber que todo objeto em Java herda o toString(), e você pode personalizá-lo.
Outro exemplo com Conta:
public class Conta {
int numero;
String cliente;
double saldo;
double limite;
Conta(int numero, String cliente) {
this.numero = numero;
this.cliente = cliente;
this.saldo = 0;
this.limite = 0;
}
@Override
public String toString() {
return "Conta " + this.numero + " | " + this.cliente
+ " | Saldo: R$" + this.saldo + " | Limite: R$" + this.limite;
}
// ... demais métodos ...
}Conta c1 = new Conta(1, "Leandro");
c1.depositar(500);
IO.println(c1); // Conta 1 | Leandro | Saldo: R$500.0 | Limite: R$0.0A classe Criatura completa (até aqui)
Segue a versão completa da classe Criatura com tudo que aprendemos até esta aula:
public class Criatura {
String nome;
int vida;
String tipo;
int forca;
// 🌅 Ritual de Nascimento completo
Criatura(String nome, int vida, String tipo, int forca) {
this.nome = nome;
this.vida = vida;
this.tipo = tipo;
this.forca = forca;
}
// 🌅 Ritual simplificado
Criatura(String nome) {
this.nome = nome;
this.vida = 100;
this.tipo = "Desconhecido";
this.forca = 10;
}
// ⚡ Poderes
void atacar(Criatura alvo) {
IO.println(this.nome + " ataca " + alvo.nome + "!");
alvo.receberDano(this.forca);
}
void receberDano(int dano) {
this.vida -= dano;
if (this.vida < 0) this.vida = 0;
}
boolean estaViva() {
return this.vida > 0;
}
// 🪪 Identidade: a criatura se apresenta
@Override
public String toString() {
return this.nome + " [" + this.tipo + "] - Vida: " + this.vida
+ " | Força: " + this.forca;
}
// ⚖️ Igualdade: critério definido pelo Criador
public boolean equals(Criatura outra) {
return this.nome.equals(outra.nome);
}
}🔮 Roteiro no Observatório Divino (BlueJ)
Passo 1 — Criar o projeto:
Abra o BlueJ e crie a classe Criatura com o código completo acima (com toString e equals). Compile a classe.
Passo 2 — Criar duas criaturas idênticas:
Clique com o botão direito na classe Criatura → new Criatura(String, int, String, int):
- Objeto 1: nome
fenix1, parâmetros:"Fênix",100,"Ave de Fogo",30 - Objeto 2: nome
fenix2, parâmetros:"Fênix",100,"Ave de Fogo",30
Duas criaturas no Altar. Parecem iguais — mas serão?
Passo 3 — Testar o toString():
Clique com o botão direito em fenix1 → selecione String toString(). O BlueJ mostrará: "Fênix [Ave de Fogo] - Vida: 100 | Força: 30". A criatura se apresentou!
Passo 4 — Testar o equals():
Clique com o botão direito em fenix1 → selecione boolean equals(Criatura). O BlueJ pedirá uma criatura para comparar — clique em fenix2 no Altar. O resultado será true — criaturas com o mesmo nome são equivalentes!
Passo 5 — Criar uma criatura diferente:
Crie uma terceira criatura: "Smaug", 200, "Dragão", 50. Agora teste fenix1.equals(smaug1) — o resultado será false.
Passo 6 — A Visão Divina (Inspect):
Inspecione fenix1 e fenix2. Observe que ambas têm a mesma essência, mas são objetos diferentes no Altar — cada uma ocupa seu próprio lugar. São gêmeas, não a mesma criatura.
Passo 7 — Testar referência compartilhada:
Agora, no Code Pad do BlueJ (View → Show Code Pad), digite:
Criatura c1 = fenix1;
Criatura c2 = fenix1;Inspecione ambas. Observe que alterar uma (ex: c1.vida = 999) altera a outra — porque são a mesma criatura.
Tips
O BlueJ é o Observatório Divino. Nesta aula, ele revelou verdades sobre identidade: duas criaturas podem parecer iguais mas serem diferentes (equals), e duas referências podem parecer diferentes mas apontar para a mesma criatura (==).