Javaのパラメータ受け渡しメカニズムとしての値渡し

Javaのパラメーター受け渡しメカニズムとしてのPass-By-Value

 

1. 前書き

メソッドに引数を渡す最も一般的な2つのモードは、「値による受け渡し」と「参照による受け渡し」です。 さまざまなプログラミング言語がこれらの概念をさまざまな方法で使用しています。 As far as Java is concerned, everything is strictly Pass-by-Value.

このチュートリアルでは、Javaがさまざまなタイプの引数を渡す方法を説明します。

2. 値渡しと参照渡し

パラメータを関数に渡すためのさまざまなメカニズムのいくつかから始めましょう。

  • 参照

  • 結果

  • 値の結果

  • name

最新のプログラミング言語で最も一般的な2つのメカニズムは、「値渡し」と「参照渡し」です。 先に進む前に、まずこれらについて説明しましょう。

2.1. 値渡し

パラメーターが値渡しの場合、呼び出し元と呼び出し先のメソッドは、相互のコピーである2つの異なる変数を操作します。 一方の変数を変更しても、もう一方は変更されません。

これは、メソッドの呼び出し中に、parameters passed to the callee method will be clones of original parameters.呼び出し先メソッドで行われた変更は、呼び出し元メソッドの元のパラメーターに影響を与えないことを意味します。

2.2. 参照渡し

パラメーターが参照渡しの場合、呼び出し元と呼び出し先は同じオブジェクトを操作します。

これは、変数が参照渡しの場合、the unique identifier of the object is sent to the method.パラメータのインスタンスメンバーに変更を加えると、元の値に変更が加えられることを意味します。

3. Javaでのパラメータの受け渡し

プログラミング言語の基本概念は、「値」と「参照」です。 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.値と参照の両方がスタックメモリに格納されます。

Javaの引数は常に値で渡されます。 メソッドの呼び出し中に、値または参照に関係なく、各引数のコピーがスタックメモリに作成され、メソッドに渡されます。

プリミティブの場合、値は単純にスタックメモリ内にコピーされ、呼び出し先メソッドに渡されます。非プリミティブの場合、スタックメモリ内の参照は、ヒープに存在する実際のデータを指します。 オブジェクトを渡すと、スタックメモリ内の参照がコピーされ、新しい参照がメソッドに渡されます。

いくつかのコード例を使用して、これが実際に動作する様子を見てみましょう。

3.1. プリミティブ型の受け渡し

Javaプログラミング言語はeight 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.

これらの仮パラメータの有効期間は、そのメソッドが実行されている間のみ持続し、返されると、これらの仮引数はスタックからクリアされ、破棄されます。

コード例を使用して、それを理解してみましょう。

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;
    }
}

これらの値がメモリにどのように格納されているかを分析して、上記のプログラムのアサーションを理解してみましょう。

  1. mainメソッドの変数「x”」と「y”」はプリミティブ型であり、それらの値はスタックメモリに直接格納されます。

  2. メソッドmodify()を呼び出すと、これらの各変数の正確なコピーが作成され、スタックメモリの異なる場所に格納されます。

  3. これらのコピーに対する変更は、それらにのみ影響し、元の変数は変更されません。

image

3.2. オブジェクト参照の受け渡し

Javaでは、すべてのオブジェクトは、ボンネットの下のヒープスペースに動的に格納されます。 これらのオブジェクトは、参照変数と呼ばれる参照から参照されます。

Javaオブジェクトは、プリミティブとは対照的に、2段階で保存されます。 参照変数はスタックメモリに保存され、参照しているオブジェクトはヒープメモリに保存されます。

オブジェクトが引数として渡されるたびに、元の参照変数と同じヒープメモリ内のオブジェクトの場所を指す参照変数の正確なコピーが作成されます。

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.ただし、渡された参照変数に新しいオブジェクトを割り当てると、元のオブジェクトには反映されません。

コード例を使用して、これを理解してみましょう。

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;
    }
}

上記のプログラムのアサーションを分析してみましょう。 同じ値1を持つオブジェクトbmodify() メソッドに渡しました。 最初、これらのオブジェクト参照は、ヒープスペース内の2つの異なるオブジェクトの場所を指しています:image

これらの参照modify()メソッドで渡されると、同じ古いオブジェクトを指すこれらの参照a1 b1のミラーコピーが作成されます。

image

modify() メソッドでは、参照a1を変更すると、元のオブジェクトが変更されます。 ただし、参照用にb1, weは新しいオブジェクトを割り当てました。 つまり、ヒープメモリ内の新しいオブジェクトを指していることになります。

b1に加えられた変更は、元のオブジェクトには何も反映されません。

image

4. 結論

この記事では、PrimitivesとNon-Primitivesの場合にパラメーターの受け渡しがどのように処理されるかを見ました。

Javaで渡すパラメータは常に値渡しであることを学びました。 ただし、プリミティブとオブジェクトのどちらを扱っているかによって、コンテキストは変わります。

  1. プリミティブ型の場合、パラメーターは値渡しです

  2. オブジェクト型の場合、オブジェクト参照は値渡しです

この記事で使用されているコードスニペットは、over on GitHubにあります。