Java Essencial: Tipos por referência, null e noções de memória

Capítulo 5

Tempo estimado de leitura: 8 minutos

+ Exercício

Tipos por referência: o que a variável realmente guarda

Em Java, muitos valores não cabem “dentro” da variável como acontece com tipos primitivos. Para objetos (como String, arrays e instâncias de classes), a variável guarda uma referência: um valor que aponta para um objeto existente na memória. A referência permite acessar o objeto (seus campos e métodos), mas não é o objeto em si.

Na prática, isso explica por que duas variáveis podem “apontar” para o mesmo objeto e por que atribuições e parâmetros podem causar efeitos colaterais quando o objeto é mutável.

Analogia útil (sem depender de detalhes internos)

  • Primitivo: a variável guarda o valor (ex.: int guarda 10).
  • Referência: a variável guarda um “endereço/ponteiro lógico” para um objeto (ex.: Cliente guarda uma referência para um objeto Cliente).

Criação de objetos com new (passo a passo)

Quando você usa new, Java cria um objeto e devolve uma referência para ele. O passo a passo mental é:

  1. Reservar memória para o novo objeto.
  2. Inicializar seus campos com valores padrão (ou com o que o construtor definir).
  3. Executar o construtor.
  4. Retornar a referência para a variável receber.
class Pessoa {    String nome;    int idade;}
Pessoa p = new Pessoa(); // p recebe uma referência para um novo objeto Pessoa

Após isso, p não “contém” a pessoa; p aponta para ela.

Diferença prática: primitivos vs referências

Atribuição

Em primitivos, a atribuição copia o valor. Em referências, a atribuição copia a referência (ou seja, passa a apontar para o mesmo objeto).

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

int a = 10;int b = a; // copia o valor 10b = 99; // não afeta a
Pessoa p1 = new Pessoa();p1.nome = "Ana";Pessoa p2 = p1; // copia a referência (p2 aponta para o mesmo objeto)p2.nome = "Bia"; // altera o mesmo objeto apontado por p1 e p2System.out.println(p1.nome); // Bia

Identidade (mesmo objeto) vs igualdade (mesmo conteúdo)

Para referências, existem duas perguntas diferentes:

  • Identidade: são o mesmo objeto? Use ==.
  • Igualdade: têm o mesmo conteúdo/estado? Use equals (quando implementado).

Exemplo com String: == vs equals

String é um objeto. Em geral, compare conteúdo com equals.

String s1 = new String("java");String s2 = new String("java");System.out.println(s1 == s2);      // false (objetos diferentes)System.out.println(s1.equals(s2)); // true (mesmo conteúdo)

Agora um caso comum: literais podem ser reutilizados internamente, então == pode “parecer funcionar” em alguns casos, mas não é uma regra segura para conteúdo.

String a = "java";String b = "java";System.out.println(a == b); // pode ser true (mesma referência reutilizada)System.out.println(a.equals(b)); // true

Regra prática: para String, use equals para comparar texto.

Exemplo com objeto simples: implementando equals

Se você quer igualdade por conteúdo em suas classes, implemente equals (e, em projetos reais, também hashCode).

class Ponto {    int x;    int y;    Ponto(int x, int y) {        this.x = x;        this.y = y;    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Ponto p = (Ponto) o;        return x == p.x && y == p.y;    }}
Ponto p1 = new Ponto(1, 2);Ponto p2 = new Ponto(1, 2);System.out.println(p1 == p2);      // false (objetos diferentes)System.out.println(p1.equals(p2)); // true (mesmo conteúdo)

Passagem de parâmetros: o que realmente é passado

Em Java, parâmetros são sempre passados por valor. A confusão acontece porque, no caso de objetos, o “valor” passado é a referência. Isso significa:

  • O método recebe uma cópia da referência.
  • Com essa cópia, ele pode modificar o objeto (se ele for mutável).
  • Mas se o método reatribuir o parâmetro para outro objeto, isso não muda a variável original de quem chamou.

Exemplo 1: método altera o objeto (efeito visível fora)

class Conta {    double saldo;}
static void depositar(Conta c, double valor) {    c.saldo += valor;}
Conta conta = new Conta();conta.saldo = 100;depositar(conta, 50);System.out.println(conta.saldo); // 150

Exemplo 2: método reatribui o parâmetro (não afeta quem chamou)

static void trocarConta(Conta c) {    c = new Conta();    c.saldo = 999;}
Conta conta = new Conta();conta.saldo = 100;trocarConta(conta);System.out.println(conta.saldo); // 100 (não mudou)

O método recebeu uma cópia da referência. Ao fazer c = new Conta(), ele só mudou a cópia local.

null: ausência de objeto

null é um valor especial que significa “nenhum objeto”. Ele só pode ser atribuído a variáveis de referência (não a primitivos).

Pessoa p = null;String nome = null;

Se você tentar acessar um membro (campo/método) através de uma referência null, ocorre NullPointerException.

Pessoa p = null;System.out.println(p.nome); // NullPointerException

Onde o null aparece com frequência

  • Variável declarada mas não inicializada com objeto.
  • Retorno de método indicando “não encontrado”.
  • Campo opcional que ainda não foi preenchido.
  • Elementos de array/coleção não preenchidos (dependendo do caso).

Padrões de checagem para evitar NullPointerException

1) Checagem explícita com if (passo a passo)

Quando um valor pode ser null, valide antes de usar:

  1. Verifique obj == null.
  2. Trate o caso (retorne, lance exceção, use padrão).
  3. Somente então acesse membros.
static int tamanhoDoNome(Pessoa p) {    if (p == null || p.nome == null) {        return 0;    }    return p.nome.length();}

2) Comparação segura de String

Para evitar NPE ao comparar texto, prefira chamar equals em um literal (que não é null):

String status = null;boolean ativo = "ATIVO".equals(status); // false, sem NPE

Evite:

// pode lançar NPE se status for nullboolean ativo = status.equals("ATIVO");

3) Falhar rápido com validação

Quando null é inválido para o seu método, é melhor acusar o erro imediatamente:

static void imprimirNome(Pessoa p) {    if (p == null) {        throw new IllegalArgumentException("pessoa não pode ser null");    }    if (p.nome == null) {        throw new IllegalArgumentException("nome não pode ser null");    }    System.out.println(p.nome);}

4) Valor padrão (quando fizer sentido)

Em alguns casos, você pode substituir null por um valor padrão:

static String nomeOuPadrao(Pessoa p) {    if (p == null || p.nome == null) return "(sem nome)";    return p.nome;}

Noções de memória: referências compartilhadas e efeitos colaterais

Quando duas variáveis guardam a mesma referência, qualquer alteração no objeto por uma delas será vista pela outra. Isso é comum ao:

  • Atribuir uma variável de referência para outra.
  • Passar um objeto para métodos que modificam seu estado.
  • Guardar o mesmo objeto em mais de um lugar (ex.: duas posições de array).

Exemplo: referência compartilhada em dois “donos”

class Endereco {    String cidade;}
class Cliente {    String nome;    Endereco endereco;}
Endereco e = new Endereco();e.cidade = "Recife";Cliente c1 = new Cliente();c1.nome = "João";c1.endereco = e;Cliente c2 = new Cliente();c2.nome = "Maria";c2.endereco = e; // compartilham o mesmo Enderecoc2.endereco.cidade = "Natal";System.out.println(c1.endereco.cidade); // Natal (efeito colateral)

Se a intenção era cada cliente ter seu próprio endereço, você precisa criar outro objeto:

c2.endereco = new Endereco();c2.endereco.cidade = "Natal";

Atividade prática: diagnosticar erros comuns com null e referências

Leia cada trecho, responda: (1) o que acontece ao executar? (2) por quê? (3) como corrigir com a menor mudança possível?

Parte A — NPE por falta de inicialização

class Produto {    String nome;}
Produto p = null;System.out.println(p.nome.toUpperCase());
  • Dica: identifique exatamente qual acesso dispara a exceção.

Parte B — NPE em comparação de String

String categoria = null;if (categoria.equals("LIVRO")) {    System.out.println("É livro");}
  • Dica: reescreva a comparação para ser null-safe.

Parte C — Igualdade vs identidade

String x = new String("ok");String y = new String("ok");if (x == y) {    System.out.println("iguais");} else {    System.out.println("diferentes");}
  • Dica: o que muda se usar equals?

Parte D — Referência compartilhada (efeito colateral inesperado)

class Carrinho {    int itens;}
Carrinho a = new Carrinho();a.itens = 1;Carrinho b = a;b.itens++;System.out.println(a.itens);
  • Dica: como criar um carrinho independente para b?

Parte E — Parâmetro reatribuído não altera o chamador

class Usuario {    String nome;}
static void renomear(Usuario u) {    u = new Usuario();    u.nome = "Novo";}
Usuario u = new Usuario();u.nome = "Antigo";renomear(u);System.out.println(u.nome);
  • Dica: para mudar o nome do mesmo objeto, o método deve alterar o campo do objeto recebido, não reatribuir o parâmetro.

Checklist de diagnóstico (use em todas as partes)

PerguntaO que verificar
Alguma referência pode ser null?Variáveis, campos, retornos de método
Estou usando == para conteúdo?Trocar por equals quando a intenção for comparar valores
Há mais de uma variável apontando para o mesmo objeto?Atribuições do tipo b = a, campos compartilhados
O método reatribui o parâmetro?Reatribuição não altera a referência do chamador

Agora responda o exercício sobre o conteúdo:

Ao atribuir uma variável de referência para outra (por exemplo, p2 = p1) e depois alterar um campo pelo segundo nome (p2), o que acontece com o objeto acessado por p1?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Em tipos por referência, a atribuição copia a referência, não o objeto. Assim, p1 e p2 apontam para a mesma instância; alterar um campo via p2 modifica o mesmo objeto que p1 acessa.

Próximo capitúlo

Java Essencial: Operadores, expressões e boas práticas de legibilidade

Arrow Right Icon
Capa do Ebook gratuito Java Essencial: Fundamentos da Linguagem e do Ecossistema (JDK, IDE, Maven)
28%

Java Essencial: Fundamentos da Linguagem e do Ecossistema (JDK, IDE, Maven)

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.