Java: passagem por referência ou por valor? Análise completa

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: a batalha eterna "por referência" vs "por valor"



Se você já escreveu em Java e passou um array ou objeto para um método, e depois se surpreendeu por que os dados originais mudaram — você não está sozinho. Essa pergunta atormenta iniciantes (e não só) desde o surgimento da linguagem. No Stack Overflow, ela acumulou milhões de visualizações, e os debates nos comentários não cessam há anos.



Hoje vamos entender de uma vez por todas: Java é "pass-by-reference" ou "pass-by-value"? Spoiler: a resposta é inequívoca, mas o diabo, como sempre, está nos detalhes. Não vamos apenas dar uma definição seca, mas mostrar com exemplos reais como a passagem de argumentos funciona em Java e por que muitos se confundem.



O que dizem os criadores do Java?



A documentação oficial e o próprio James Gosling (criador do Java) não deixam espaço para debates: Java sempre passa argumentos por valor (pass-by-value). Sem exceções. Nem para primitivos, nem para objetos.



Mas então por que o código altera objetos? Por que um List ou array passado para um método é modificado? Vamos entender.



Pass-by-value: o que isso significa exatamente?



Para começar — uma definição clara. Pass-by-value significa que, ao chamar um método, uma cópia do valor da variável é criada, e é essa cópia que é passada para o método. A variável original (no código chamador) permanece intocada.



Pass-by-reference é quando uma referência à variável original é passada para o método, e o método pode alterar diretamente seu valor (por exemplo, substituir o objeto). Isso não existe em Java.



Agora — aos exemplos.



Exemplo 1: tipos primitivos (int, double, boolean)



Com primitivos, tudo é óbvio. Vamos escrever um código simples:



public class Main {    public static void main(String[] args) {        int x = 10;        changePrimitive(x);        System.out.println("Após o método x = " + x); // 10    }

public static void changePrimitive(int value) { value = 20; // altera a cópia }}


Saída:

Após o método x = 10


Tudo lógico: passamos uma cópia do número 10, dentro do método alteramos a cópia para 20, mas o original permaneceu como estava. Isso é pass-by-value puro.



Exemplo 2: objetos (strings, listas, classes próprias)



Agora, a parte mais interessante. Veja:



public class Main {    public static void main(String[] args) {        StringBuilder sb = new StringBuilder("Hello");        changeStringBuilder(sb);        System.out.println("Após o método: " + sb); // Hello World!    }

public static void changeStringBuilder(StringBuilder builder) { builder.append(" World!"); // altera o conteúdo do objeto }}


Saída:

Após o método: Hello World!


«Ahá! — você dirá. — O objeto mudou! Então, isso é pass-by-reference!»



Não. Essa é a armadilha em que 90% dos iniciantes caem. Vamos analisar o que realmente aconteceu.



O que está armazenado na variável sb?



Em Java, uma variável de tipo objeto (por exemplo, StringBuilder, List, String) não armazena o objeto em si. Ela armazena uma referência (endereço) para o objeto na memória. O objeto em si está em algum lugar no heap.



Quando você passa sb para um método, você passa uma cópia dessa referência. Ou seja, uma cópia do endereço. Tanto o original quanto a cópia apontam para o mesmo objeto. Portanto, quando você altera o conteúdo do objeto através da cópia da referência, essas alterações são visíveis também através da referência original.



Mas! Se você tentar atribuir um novo objeto ao parâmetro dentro do método, a referência original não mudará. Vamos provar:



public class Main {    public static void main(String[] args) {        StringBuilder sb = new StringBuilder("Hello");        changeReference(sb);        S                

Blogs

Book Recommendations