はじめに
多くの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
}
}
例プログラムを実行すると、次の出力が得られます:
OutputAfter 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()
メソッドをred
とblue
変数を引数として呼び出すと、2つの新しいオブジェクト変数、o1
とo2
が作成されます。o1
とo2
もそれぞれメモリの場所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 } // メソッド終了
-
o1
とo2
の値が入れ替わりますが、それらの値はred
とblue
のメモリ位置のコピーであるため、red
とblue
の色の値には変化はありません。
変数はオブジェクトへの参照を格納しているため、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