CodeGym /Cursos /JAVA 25 SELF /Passagem de parâmetros por valor e por referência

Passagem de parâmetros por valor e por referência

JAVA 25 SELF
Nível 8 , Lição 4
Disponível

1. Introdução

Em diferentes linguagens de programação, existem duas formas principais de passagem de parâmetros para funções (ou métodos):

  • Por valor: a função recebe uma cópia do valor da variável. Se a função alterar o parâmetro, o original não muda.
  • Por referência: a função recebe uma “referência” (ponteiro) para o objeto original. As mudanças dentro da função se refletem no original.

Em Java, TODOS os parâmetros são passados por valor!
Mas há um detalhe: quando passamos tipos primitivos (int, double, boolean etc.), o que é copiado é o próprio valor. Já quando passamos tipos de referência (por exemplo, arrays ou objetos), o que é copiado é o valor da referência (isto é, o “endereço” do objeto na memória), e não o objeto em si.

Se você sentir que o cérebro está derretendo, não se preocupe — já vamos organizar tudo e mostrar com exemplos!

2. Tipos primitivos: o valor é copiado

Vamos começar com o simples: o que acontece se tentarmos alterar o valor de uma variável do tipo int dentro de um método?

public class Demo 
{
    public static void main(String[] args) 
    {
        int number = 5;
        changeValue(number);
        System.out.println(number); // O que será impresso?
    }

    public static void changeValue(int n) 
    {
        n = 42;
    }
}

Resultado: será impresso 5.

Por quê?
O método changeValue recebe uma CÓPIA do valor da variável number. Dentro do método, alteramos apenas a cópia (n); a variável original number em main permanece intocada.

Analogia
Imagine que você deu a um amigo uma fotocópia do passaporte, e não o original. O amigo pode desenhar bigodes na cópia o quanto quiser — seu passaporte verdadeiro continua o mesmo!

3. Tipos de referência: a referência é copiada

Agora, vamos tentar o mesmo com um array:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] numbers = {1, 2, 3};
        changeFirst(numbers);
        System.out.println(numbers[0]); // O que será impresso?
    }

    public static void changeFirst(int[] arr) 
    {
        arr[0] = 99;
    }
}

Resultado: será impresso 99.

Por quê?
O método changeFirst recebe uma cópia da REFERÊNCIA para o array numbers. Ambos os nomes (numbers em main e arr no método) apontam para o mesmo array na memória. Por isso, quando alteramos um elemento do array dentro do método, a alteração fica “visível” fora.

Visualização

main: numbers ──► [1, 2, 3]
                   ▲
                   │
changeFirst: arr ──┘
A cópia da referência aponta para o mesmo objeto na memória

Ao alterar arr[0], na verdade estamos alterando numbers[0], porque ambos apontam para o mesmo objeto.

4. Mas se você trocar a própria referência — o objeto original não mudará!

Vamos tentar assim:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] numbers = {1, 2, 3};
        replaceArray(numbers);
        System.out.println(numbers[0]); // O que será impresso?
    }

    public static void replaceArray(int[] arr) 
    {
        arr = new int[] {10, 20, 30};
    }
}

Resultado: imprimirá 1.

Por quê?
No método replaceArray, a variável arr passa a apontar para um novo array, mas isso não afeta a variável numbers em main.
Elas estavam ligadas por uma referência, mas, quando dentro do método fazemos arr = ..., isso é apenas uma mudança local.

Analogia
É como se você e seu amigo tivessem a mesma chave de casa. O amigo faz uma nova chave para si, mas sua chave antiga continua a mesma — e a porta continua sendo a mesma.

5. Exemplos para fixar: não é possível “trocar de lugar” dois int via método

Muitos iniciantes tentam escrever um método para trocar dois números de lugar, mas não conseguem:

public class Demo 
{
    public static void main(String[] args) 
    {
        int a = 5, b = 10;
        swap(a, b);
        System.out.println(a + " " + b); // Esperado: 10 5, e na realidade?
    }

    public static void swap(int x, int y) 
    {
        int temp = x;
        x = y;
        y = temp;
    }
}

Resultado: serão impressos 5 10.

Por quê?
Porque em swap chegam cópias das variáveis a e b. Tudo o que fazemos dentro de swap afeta apenas essas cópias, não os originais.

6. Mas com arrays dá para alterar o conteúdo!

Veja a forma correta de trocar valores de lugar em um array:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] arr = {5, 10};
        swap(arr);
        System.out.println(arr[0] + " " + arr[1]); // Esperado: 10 5
    }

    public static void swap(int[] arr) 
    {
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
    }
}

Resultado: imprimirá 10 5.

Por quê?
Porque alteramos o conteúdo do objeto (array) para o qual apontam tanto a variável original quanto o parâmetro do método.

7. Demonstração: objetos e seus campos

Já vimos os arrays. E quanto aos objetos de classes próprias? É exatamente igual!

class Box 
{
    int value;
}

public class Demo
{
    public static void main(String[] args) 
    {
        Box box = new Box();
        box.value = 7;
        changeBox(box);
        System.out.println(box.value); // O que será impresso?
    }

    public static void changeBox(Box b) 
    {
        b.value = 42;
    }
}

Resultado: imprimirá 42.

Por quê?
Porque alteramos um campo do objeto para o qual a referência aponta.

O que acontece ao passar parâmetros para métodos

O que passamos Como é copiado Pode alterar dentro do método? As alterações são visíveis fora?
int, double e outros Cópia do valor Sim, mas apenas a cópia Não
Array Cópia da referência Sim, se alterar os elementos Sim
Objeto (classe) Cópia da referência Sim, se alterar os campos Sim
Array/objeto Cópia da referência Não, se atribuir uma nova referência Não

8. Erros comuns

Erro nº 1: esperar que um int mude após a chamada do método. Muitos iniciantes pensam que, se passarem uma variável do tipo int a um método, poderão mudar seu valor dentro do método. Isso é incorreto: muda-se apenas a cópia!

Erro nº 2: tentar trocar de lugar dois int via método. Como já vimos, isso é impossível — apenas as cópias das variáveis trocam de lugar; os originais não são afetados.

Erro nº 3: tentar “substituir” um array via método. Se, dentro do método, você atribuir ao parâmetro-array um novo array (arr = new int[]{...}), isso não afetará o array original. Para “substituir” o array — é preciso retornar um novo array do método e atribuí-lo à variável no código chamador.

Erro nº 4: esquecer que é possível alterar o conteúdo do objeto. Se você passar um objeto (ou um array) — não se esqueça de que quaisquer mudanças em seus campos/elementos serão visíveis do lado de fora!

Erro nº 5: confusão com null. Se você passar ao método uma referência igual a null e tentar acessar seus campos — você terá um NullPointerException. Fique atento!

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION