Passagem por valor como um mecanismo de passagem de parâmetros em Java

Passagem por valor como um mecanismo de passagem de parâmetros em Java

 

1. Introdução

Os dois modos mais comuns de passar argumentos para métodos são "passagem por valor" e "passagem por referência". Diferentes linguagens de programação usam esses conceitos de maneiras diferentes. As far as Java is concerned, everything is strictly Pass-by-Value.

Neste tutorial, vamos ilustrar como Java passa argumentos para vários tipos.

2. Passagem por valor vs passagem por referência

Vamos começar com alguns dos diferentes mecanismos para passar parâmetros para funções:

  • valor

  • referência

  • resultado

  • valor-resultado

  • name

Os dois mecanismos mais comuns em linguagens de programação modernas são "Passagem por valor" e "Passagem por referência". Antes de prosseguirmos, vamos discutir isso primeiro:

2.1. Pass-by-Value

Quando um parâmetro é transmitido por valor, o chamador e o método de chamada operam em duas variáveis ​​diferentes, que são cópias uma da outra. Quaisquer alterações em uma variável não modificam a outra.

Isso significa que ao chamar um método,parameters passed to the callee method will be clones of original parameters. Qualquer modificação feita no método do callee não terá efeito nos parâmetros originais do método do chamador.

2.2. Passagem por referência

Quando um parâmetro é passado por referência, o chamador e o chamado operam no mesmo objeto.

Isso significa que quando uma variável é passada por referência,the unique identifier of the object is sent to the method. Quaisquer alterações nos membros da instância do parâmetro resultarão em que a alteração seja feita no valor original.

3. Passagem de parâmetro em Java

Os conceitos fundamentais em qualquer linguagem de programação são "valores" e "referências". Em Java,Primitive variables store the actual values, whereas Non-Primitives store the reference variables which point to the addresses of the objects they’re referring to. Ambos os valores e referências são armazenados na memória da pilha.

Os argumentos em Java são sempre passados ​​por valor. Durante a invocação do método, uma cópia de cada argumento, seja um valor ou uma referência, é criada na memória da pilha que é então passada ao método.

No caso de primitivas, o valor é simplesmente copiado dentro da memória da pilha, que é passada ao método chamado; no caso de não primitivos, uma referência na memória da pilha aponta para os dados reais que residem no heap. Quando passamos um objeto, a referência na memória da pilha é copiada e a nova referência é passada para o método

Vamos agora ver isso em ação com a ajuda de alguns exemplos de código.

3.1. Passando Tipos Primitivos

A linguagem de programação Java apresentaeight primitive data types. Primitive variables are directly stored in stack memory. Whenever any variable of primitive data type is passed as an argument, the actual parameters are copied to formal arguments and these formal arguments accumulate their own space in stack memory.

A vida útil desses parâmetros formais dura apenas enquanto esse método estiver em execução e, ao retornar, esses argumentos formais são limpos da pilha e descartados.

Vamos tentar entendê-lo com a ajuda de um exemplo de código:

public class PrimitivesUnitTest {

    @Test
    public void whenModifyingPrimitives_thenOriginalValuesNotModified() {

        int x = 1;
        int y = 2;

        // Before Modification
        assertEquals(x, 1);
        assertEquals(y, 2);

        modify(x, y);

        // After Modification
        assertEquals(x, 1);
        assertEquals(y, 2);
    }

    public static void modify(int x1, int y1) {
        x1 = 5;
        y1 = 10;
    }
}

Vamos tentar entender as afirmações do programa acima, analisando como esses valores são armazenados na memória:

  1. As variáveis ​​“x”e“y” no método principal são tipos primitivos e seus valores são armazenados diretamente na memória da pilha

  2. Quando chamamos o métodomodify(), uma cópia exata para cada uma dessas variáveis ​​é criada e armazenada em um local diferente na memória da pilha

  3. Qualquer modificação nessas cópias afeta apenas eles e deixa as variáveis ​​originais inalteradas

image

3.2. Passando referências de objeto

Em Java, todos os objetos são armazenados dinamicamente no espaço Heap sob o capô. Esses objetos são referidos a partir de referências chamadas variáveis ​​de referência.

Um objeto Java, em contraste com Primitivos, é armazenado em dois estágios. As variáveis ​​de referência são armazenadas na memória de pilha e o objeto ao qual elas se referem, são armazenados em uma memória Heap.

Sempre que um objeto é passado como um argumento, uma cópia exata da variável de referência é criada, apontando para a mesma localização do objeto na memória heap que a variável de referência original.

As a result of this, whenever we make any change in the same object in the method, that change is reflected in the original object. No entanto, se alocarmos um novo objeto para a variável de referência passada, ele não será refletido no objeto original.

Vamos tentar compreender isso com a ajuda de um exemplo de código:

public class NonPrimitivesUnitTest {

    @Test
    public void whenModifyingObjects_thenOriginalObjectChanged() {
        Foo a = new Foo(1);
        Foo b = new Foo(1);

        // Before Modification
        assertEquals(a.num, 1);
        assertEquals(b.num, 1);

        modify(a, b);

        // After Modification
        assertEquals(a.num, 2);
        assertEquals(b.num, 1);
    }

    public static void modify(Foo a1, Foo b1) {
        a1.num++;

        b1 = new Foo(1);
        b1.num++;
    }
}

class Foo {
    public int num;

    public Foo(int num) {
        this.num = num;
    }
}

Vamos analisar as afirmações do programa acima. Passamos os objetosandb emmodify() method que tem o mesmo valor1. Inicialmente, essas referências de objeto estão apontando para dois locais de objeto distintos em um espaço de heap:image

Quando essas referênciasand são passadas no métodomodify(), ele cria cópias espelhadas dessas referênciasa1 andb1 que apontam para os mesmos objetos antigos:

image

No métodomodify() , quando modificamos a referênciaa1, ele muda o objeto original. No entanto, para uma referênciab1, we atribuiu um novo objeto. Portanto, agora está apontando para um novo objeto na memória heap.

Qualquer alteração feita emb1 não refletirá nada no objeto original:

image

4. Conclusão

Neste artigo, vimos como a passagem de parâmetros é tratada no caso de primitivas e não primitivas.

Aprendemos que a passagem de parâmetros em Java é sempre Pass-by-Value. No entanto, o contexto muda dependendo se estamos lidando com Primitivos ou Objetos:

  1. Para tipos primitivos, os parâmetros são passados ​​por valor

  2. Para tipos de objeto, a referência do objeto é passada por valor

Os trechos de código usados ​​neste artigo podem ser encontradosover on GitHub.