Objetos como Parâmetros em Java
Entender como o Java passa argumentos para métodos é fundamental. A maneira como a linguagem lida com tipos primitivos e com objetos é diferente, e essa distinção é crucial para evitar bugs e comportamentos inesperados.
Tipos Primitivos
Quando você passa um tipo primitivo (int
, double
, boolean
, etc.) para um método, o Java cria e passa uma cópia completa do valor. Isso significa que o método trabalha com uma duplicata, e qualquer alteração feita nesta cópia dentro do método não afeta em nada a variável original.
public class Program {
public static void main(String[] args) {
int number = 10;
System.out.println("Antes da chamada: " + number); // Saída: 10
change(number);
// O valor original de 'number' permanece inalterado.
System.out.println("Depois da chamada: " + number); // Saída: 10
}
static void change(int value) {
// 'value' é uma cópia independente de 'number'.
value = 30;
}
}
Objetos
Quando você passa um objeto para um método, o comportamento é diferente. O método recebe uma referência que aponta para o local do objeto original na memória. Isso permite que o método interaja diretamente com esse objeto.
Isso leva a dois cenários importantes.
Cenário 1: Modificando o Estado de um Objeto
Um método pode usar a referência que recebeu para alterar os campos (o estado) do objeto original. Essa mudança será visível fora do método, pois o objeto modificado é o mesmo.
public class Program {
public static void main(String[] args) {
Person kate = new Person("Kate");
System.out.println("Nome antes: " + kate.getName()); // Saída: Kate
// Passamos o objeto 'kate' para o método.
changeName(kate);
// A mudança feita dentro do método é refletida aqui.
System.out.println("Nome depois: " + kate.getName()); // Saída: Alice
}
static void changeName(Person p) {
// O parâmetro 'p' aponta para o mesmo objeto que 'kate'.
p.setName("Alice");
}
}
class Person {
private String name;
public Person(String name) { this.name = name; }
public void setName(String name) { this.name = name; }
public String getName() { return this.name; }
}
Cenário 2: A Tentativa de Substituir o Objeto
Aqui está um detalhe crucial. Se você tentar atribuir um objeto completamente novo ao parâmetro dentro de um método, a variável original fora do método não será afetada.
A atribuição p = new Person("Alice");
apenas faz com que o parâmetro p
(que é uma variável local ao método) aponte para um novo objeto. A variável original, kate
, continua apontando para o objeto com o qual foi inicializada.
public class Program {
public static void main(String[] args) {
Person kate = new Person("Kate");
System.out.println("Nome antes: " + kate.getName()); // Saída: Kate
changePerson(kate);
// A variável 'kate' nunca foi alterada e ainda aponta para o objeto original.
System.out.println("Nome depois: " + kate.getName()); // Saída: Kate
}
static void changePerson(Person p) {
// 'p' é reatribuído para um NOVO objeto. O objeto original não é afetado.
p = new Person("Alice");
}
}
Conclusão Final
- Para primitivos: O método recebe uma cópia do valor. A variável original não pode ser alterada.
- Para objetos: O método pode modificar o estado do objeto original, mas não pode substituir o objeto original por um novo de forma que a variável de fora do método seja afetada.
📝 Exercícios
Tarefa
Descrição: Analise atentamente o código abaixo. Qual será a saída final impressa no console para livroA
e livroB
? Rastreie as mudanças passo a passo para encontrar a resposta.
Código inicial:
public class Main {
public static void main(String[] args) {
Livro livroA = new Livro("A Arte da Guerra");
Livro livroB = new Livro("O Príncipe");
System.out.println("--- Estado Inicial ---");
System.out.println("Livro A: " + livroA.getTitulo());
System.out.println("Livro B: " + livroB.getTitulo());
misturar(livroA, livroB);
System.out.println("\n--- Estado Final ---");
System.out.println("Livro A: " + livroA.getTitulo());
System.out.println("Livro B: " + livroB.getTitulo());
}
public static void misturar(Livro x, Livro y) {
x.setTitulo("O Capital");
y = x;
y.setTitulo("A Riqueza das Nações");
}
}
class Livro {
private String titulo;
public Livro(String titulo) { this.titulo = titulo; }
public String getTitulo() { return this.titulo; }
public void setTitulo(String titulo) { this.titulo = titulo; }
}
Qual será a saída final?
Resposta
--- Estado Final --- Livro A: A Riqueza das Nações Livro B: O Príncipe
main
, a variável livroA
aponta para um objeto com o título "A Arte da Guerra", e livroB
aponta para outro objeto com o título "O Príncipe".
misturar(livroA, livroB)
é chamado, os parâmetros x
e y
recebem cópias das referências. Portanto, inicialmente, x
aponta para o mesmo objeto que livroA
, e y
aponta para o mesmo objeto que livroB
.
x.setTitulo("O Capital");
, usa a referência x
para modificar o objeto original de livroA
, alterando seu título para "O Capital".
y = x;
, é o ponto crucial. Aqui, a referência local y
é reatribuída. Ela para de apontar para o objeto de livroB
e passa a apontar para o mesmo objeto que x
aponta (que é o objeto original de livroA
). É importante notar que esta operação não afeta a variável livroB
original no método main
, que continua apontando para "O Príncipe".
y.setTitulo("A Riqueza das Nações");
, usa a referência y
para alterar o título do objeto para o qual ela agora aponta. Como y
foi redirecionada para o objeto de livroA
, é este objeto que tem seu título alterado para "A Riqueza das Nações".
main
, livroA
reflete a última alteração feita em seu objeto, exibindo "A Riqueza das Nações", e livroB
, que nunca teve seu objeto modificado, ainda exibe "O Príncipe".