プログラミング言語の一つの壁である、参照という考え方。
何度説明を聞いてもなかなか理解出来ない人もいると思います。
もし、理解したつもりになっていても
いざプログラミング中に「どうだったっけ?」と思った事はありませんか?
実は、参照渡し・値渡しには理解するための手順とポイントがあります。
しっかりとポイントを押さえて、意味を理解する事で本質的な理解が出来るので、もう忘れてしまう事はありません。
今日で参照渡しと値渡しについて悩む日は最後です。
図解でわかりやすく解説します!
ポイントは2つです!
- プリミティブ型の理解
- 変数に格納されている値の持ち方の違い
まずはプリミティブ型について理解しよう
変数にはプリミティブ型(基本データ型)と参照型の2種類があります。
プリミティブ型は基本データ型と呼ばれていて、全部で8種類あります。
byte | 8bit |
---|---|
short | 16bit |
int | 32bit |
long | 64bit |
float | 32bit |
double | 64bit |
char | 16bit |
boolean | 1bit |
ご存知の通り、byte型からlogn型までは整数を扱い、float型とdouble型は少数を扱い、char型は文字、boolean型は真偽値を扱います。
プリミティブ型の変数の特徴は値そのものだけを保持している事です。
一方、参照型はこれらのプリミティブ型を扱いやすいように包んだラッパークラスです。
例えば、有名な参照型の変数に「String型」という文字列を格納できる型がありますが
実は中身はプリミティブ型のcharの配列になっていて、1文字ずつ配列で文字列を保持しています。
参照型の変数の特徴は、あくまでも「参照型の変数」というオブジェクトなので、値に対する操作が可能です。
例えば、「String型」の変数なら次のようにisEmpty()で変数が空かどうかの判定が出来ますよね?
String str = "str"; str.isEmpty(); // true
逆にプリミティブ型の変数には、char.isEmpty()のような判定は出来ません。
変数に格納されている値の持ち方の違いを理解しよう
参照型も結局中身はプリミティブ型だという事を理解頂けたと思います。
つまり、変数に格納されている値はメモリに記録される時には同じ形式で記録されているという事です。
プログラム上の変数に格納された値は、プログラム実行中はメモリ領域に保存し、保存している値を使いまわしています。
次の画像は、整数値を保持出来るint型と複数の整数値を保持出来るint型の変数の値の持ち方の違いを表しています。

int型もint型の配列も保持している値は一つずつメモリに保存されています。
しかし、それぞれの変数が持っている値は異なっています。
プリミティブ型のintの変数aはメモリに保存した整数値の「100」を保持しているのに対して、参照型のintの配列bはメモリに保存された整数値の最初の値が保存されている場所の情報(アドレス)を保持しています。
つまり、プリミティブ型と参照型の違いは、変数に格納された値の持ち方が異なっているという事です。
プリミティブ型は値そのものを持っている。
参照型は値が格納された場所の情報(アドレス)を持っている。
参照渡しと値渡し
ここまで、変数の種類の違い(プリミティブ型・参照型)とそれぞれの変数が持つ値について解説してきました。
本題の、参照渡しと値渡しを理解するにはここまでの知識が必要不可欠です。
ではさっそく本題に入っていきます。
まず、参照渡しと値渡しが行われるタイミングですが、メソッドの引数に変数を与えた時に参照渡しか値渡しが行われます。
値渡し
値渡しは、引数にプリミティブ型の変数を与えた時に行われます。
実際にやっている事は、値のコピーを作成してメソッドに渡しています。
次の例では、変数aの値を表示すると100が表示されます。
int a = 100; replaceNumber(a); System.out.println(a); // a = 100 public static void replaceNumber(int b) { b = 200; }


この例では、整数値の100を格納した変数aをreplaceNumberメソッドの引数として渡しています。
この時、変数bは変数aからコピーされた値が格納されています。
replaceNumberメソッドではコピーされて新たに作られた変数bに対して200を代入しています。
値渡しだと変数aと変数bは別々の変数として扱われるので、repaceNumberメソッドを実行した後に変数aの値を表示させると整数値の100が表示されます。
このように、引数にコピーした値が渡され、その値はメソッド内で独立している状態が値渡しです。
参照渡し
参照渡しは引数に参照型の変数を与えた時に行われます。
値渡しと違って、値のコピーを渡すのではなく、値を格納しているメモリの場所情報(アドレス)を渡しています。
int a[] = new int[] {10, 20, 30}; replaceNumber(a); System.out.println(Arrays.toString(a)); // a = 100, 20, 30 public static void replaceNumber(int[] b) { b[0] = 100; }


この例では、整数値の10, 20, 30を格納した配列の変数aをreplaceNumberメソッドの引数として渡しています。
この時、変数bは変数aが持つメモリの場所情報(アドレス)がコピーされます。
replaceNumberメソッドではアドレスがコピーされて新たに作られた変数bに格納されている配列の0番目に100を代入しています。
変数aも変数bも同じアドレスを持っているため、変数bの持つアドレスに格納されている値が変更されると、変数aの持つアドレスに格納されている値も変更されてしまいます。
そのため、replaceNumberメソッドを実行した後に変数aの配列が持つ値を表示させると、「100, 20, 30」になります。
このように、引数に渡す値がメモリ上のアドレスの場合が参照渡しです。
厳密には、参照型はアドレスという値を持っているので値渡しでもありますが、ここでは混乱を避けるために参照渡しと呼んでいます。
まとめ
メソッドの引数に値を渡している場合は値渡し、メモリ上の場所情報(アドレス)を渡している場合は参照渡しと呼ぶ事がわかりました。
また、値渡しが行われる変数の型はプリミティブ型(基本データ型)、参照渡しが行われる変数の型は参照型でした。
値渡しと参照渡しを理解してプログラミングを行う事で、思わぬバグを回避する事が出来ます。
また、これらの理解は良いプログラミングを行うためには必須の知識です。
良いプログラミングとは、後から改善や改良がしやすくバグの発生しにくい構造になっている変更に強いソースコードを書く事です。

今回理解した値渡しと参照渡しが理解出来たら、少しステップアップしてイミュータブルな実装についても理解することをオススメします。
