Javaは値渡しであり、参照渡しではありません

はじめに

多くのJavaプログラマーがJavaが値渡しなのか参照渡しなのか疑問に思っています。この記事では、Javaが常に値渡しである理由をまとめています。

まず、値渡しと参照渡しとは何を意味するのでしょうか?

  • 値渡し:メソッドのパラメータの値が別の変数にコピーされ、そのコピーされたオブジェクトがメソッドに渡されます。メソッドはそのコピーを使用します。
  • 参照渡し:実際のパラメータへのエイリアスまたは参照がメソッドに渡されます。メソッドは実際のパラメータにアクセスします。

これらの用語に関する混乱は、Javaのオブジェクト参照の概念に起因することがよくあります。技術的には、Javaは常に値渡しです。なぜなら、変数がオブジェクトへの参照を保持しているかもしれませんが、そのオブジェクト参照はオブジェクトのメモリ内の位置を表す値であり、そのためオブジェクト参照は値渡しされます。

参照データ型とプリミティブデータ型の両方が値渡しで渡されます。Javaのデータ型についてもっと詳しく学びましょう

データ型を理解するだけでなく、Javaのメモリ割り当てについても理解することが重要です。なぜなら、参照データ型とプリミティブデータ型は異なる方法で格納されるからです。

値渡しのデモンストレーション

以下の例は、Javaで値がどのように渡されるかを示しています。

この例プログラムは、次のクラスを使用しています:

public class Balloon {

	private String color;

	public Balloon() {}
	
	public Balloon(String c) {
		this.color = c;
	}
	
	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
}

次の例のプログラムは、swap()というジェネリックメソッドを使用して、2つの変数を交換します。もう1つのメソッド、changeValue()は、変数の値を変更しようとします。

public class Test {

	public static void main(String[] args) {

		Balloon red = new Balloon("Red"); // メモリリファレンス = 50
		Balloon blue = new Balloon("Blue"); // メモリリファレンス = 100
		
		swap(red, blue);
		System.out.println("After the swap method executes:");
		System.out.println("`red` color value = " + red.getColor());
		System.out.println("`blue` color value = " + blue.getColor());
		
		changeValue(blue);
		System.out.println("After the changeValue method executes:");
		System.out.println("`blue` color value = " + blue.getColor());
		
	}

	// ジェネリックな交換メソッド
	public static void swap(Object o1, Object o2){
		Object temp = o1;
		o1 = o2;
		o2 = temp;
	}

	private static void changeValue(Balloon balloon) { // balloon = 100
		balloon.setColor("Red"); // balloon = 100
		balloon = new Balloon("Green"); // balloon = 200
		balloon.setColor("Blue"); // balloon = 200
	}

}

例プログラムを実行すると、次の出力が得られます:

Output
After the swap method executes: 'red' color value = Red 'blue' color value = Blue After the changeValue method executes: 'blue' color value = Red

出力から、swap()メソッドが元のオブジェクトの色の値を交換しなかったことがわかります。これは、swap()メソッドが元のオブジェクト参照値のコピーに対してのみ操作を行うため、Javaが値渡しであることを示しています。

このswap()メソッドのテストは、値渡しまたは参照渡しを行うかを確認するために、どのプログラミング言語でも使用できます。

例のswap()メソッドの説明

クラスのインスタンスを作成するためにnew演算子を使用すると、オブジェクトが作成され、変数にはオブジェクトが保存されているメモリ内の場所が含まれます。

Balloon red = new Balloon("Red");
Balloon blue = new Balloon("Blue");

  • redがメモリの場所50を指し、blueがメモリの場所100を指していると仮定し、これらが両方のBalloonオブジェクトのメモリの場所です。

  • クラスがswap()メソッドをredblue変数を引数として呼び出すと、2つの新しいオブジェクト変数、o1o2が作成されます。 o1o2もそれぞれメモリの場所50と100を指します。

  • 次のコードスニペットは、swap()メソッド内で何が起こるかを説明しています。

    public static void swap(Object o1, Object o2) { // o1 = 50, o2 = 100
    	Object temp = o1; // o1のオブジェクト参照値をtempに割り当てます:temp = 50, o1 = 50, o2 = 100
    	o1 = o2; // o2のオブジェクト参照値をo1に割り当てます:temp = 50, o1 = 100, o2 = 100
    	o2 = temp; // tempのオブジェクト参照値をo2に割り当てます:temp = 50, o1 = 100, o2 = 50
    } // メソッド終了
    
  • o1o2の値が入れ替わりますが、それらの値はredblueのメモリ位置のコピーであるため、redblueの色の値には変化はありません。

変数はオブジェクトへの参照を格納しているため、Javaは参照渡しを行っていると誤解することがよくあります。しかし、実際には参照のコピーを渡しているため、Javaは値渡しです。

例のchangeValue()メソッドの説明

次の方法は、例題プログラム内のblue変数で参照されるオブジェクトの色値を変更します:

private static void changeValue(Balloon balloon) { // バルーン = 100
	balloon.setColor("Red"); // バルーン = 100
	balloon = new Balloon("Green"); // バルーン = 200
	balloon.setColor("Blue"); // バルーン = 200
}

以下は、changeValue()メソッド内で何が起こるかのステップバイステップの説明です:

  • クラスはblue変数に対してchangeValue()メソッドを呼び出します。この変数はメモリ位置100を参照しています。最初の行では、メモリ位置100を指す参照が作成されます。メモリ位置100のオブジェクトの色値が"Red"に変更されます。

  • 2行目では新しいオブジェクト(色値が"Green")が作成されます。新しいオブジェクトはメモリ位置200にあります。balloon変数に対して実行される以降のメソッドは、メモリ位置200のオブジェクトに影響を与え、メモリ位置100のオブジェクトに影響を与えません。新しいballoon変数が1行目で作成された参照を上書きし、1行目のballoon参照はこのメソッド内ではもうアクセスできません。

  • 3行目は、メモリの位置200にある新しいBalloonオブジェクトの色の値を"Blue"に変更しますが、メモリの位置100にあるblueが参照する元のオブジェクトには影響しません。これが、例のプログラムの出力の最後の行がblue color value = Redを印刷する理由です。

結論

この記事では、Javaが値渡しである理由について学びました。さらなるJavaのチュートリアルで学習を続けてください。

Source:
https://www.digitalocean.com/community/tutorials/java-is-pass-by-value-and-not-pass-by-reference