参照型変数とは
変数宣言しただけのオブジェクトはインスタンスを持ちません。
newによってインスタンス化してはじめてデータを扱うことができます。
では、宣言した変数はいったい何を意味するのでしょうか?
これは「参照型変数」と言い、インスタンスのアドレス(場所)を格納しています。
【参照型変数の値の確認】
public class Sample2{ public static void main(String[ ] args){ SomeObject obj1 = new SomeObject( ); System.out.println(obj1); } }
public class SomeObject{ }
実行結果
SomeObject@15db9742
「SomeObject@何らかの値」という形式での出力が得られたはずです。
この@以降の値を「ハッシュ値」といいます。
ポイント!ハッシュ値
ハッシュ値は、アドレスをもとに導き出された値なのでアドレスそのものとは少し違います。
そして、ただ単にオブジェクトを識別するための値なので、この結果と同じ値になるとは限りません。
しかし、オブジェクトの変数がインスタンスへのアドレスを格納しているイメージはつかみやすくなったのではないでしょうか。
15db9742という場所にインスタンスが格納されているのです。
今まで使用してきたオブジェクトは「オブジェクト名.メンバ名」
としてメンバにアクセスできました(公開されているものに限ります)。
これは、オブジェクトの変数→アドレス→インスタンス→その中のメンバという順番でアクセスしていたのです。
ポイント!
Javaではこのように、参照値からインスタンスのデータに間接的にアクセスすることを自動的に行っています。
言語によってはこの参照値を自分で調べ、参照先のデータを確認・・・ということをしなければならないものもあります。
例えばC言語のような言語では、参照値を保存するための変数をポインタと呼びます。
昔よく言われていたのが「ポインタは初心者にはちょっと難しいが、Javaにはそれがないので初心者にとっつきやすい」ということなのですが、あまり正しくありません。実際は「ポインタは存在するが、普段は意識しなくても使える」というところです。
Javaでは、オブジェクトはすべてポインタです。しかし、今まで見てきたようにあまり気にしなくても使うことができます。
自動的にそのアドレスのインスタンスを参照してくれるのです。
参照型変数の特性
基本データ型の変数は参照値ではなく、変数に直接データが入っています。
これと参照値は大きな違いがあります。
例えば、メソッドの引数としてデータを渡した場合を考えてみましょう。
渡した先のメソッド内でどのような現象が起こるでしょうか。
基本データ型の変数と、新しく作ったクラスのオブジェクトについて考えてみます。
基本データ型intの変数aを宣言したとします。
aは4バイト(intのサイズ)のメモリを確保し、そこに直接データを格納します。
それに対してMyObjectクラスの変数objは、宣言したときには上図のobj分のメモリしか確保されません。
newしたときに、このオブジェクトで使いたいデータを格納するための領域が確保されます。
つまりobjには目的となるデータは入っていないのです。
「データがどこにあるか」という情報が入っています。
今回はこの情報を引数に渡すということを行ってみましょう。
【参照型変数を引数として渡す】
public class RefSample{ public static void main(String[ ] args){ //基本データ型のデータを宣言 int a = 10; //オブジェクトを宣言 MyObject obj = new MyObject( ); //それぞれの変数を渡す foo(a, obj); System.out.println(a); System.out.println(obj.x); } public static void foo(int a, MyObject obj){ //基本データ型のデータを変更 a++; //オブジェクト内のデータを変更 obj.x++; } }
public class MyObject{ public int x = 20; }
実行結果
10 21
基本データであるaの値は初期値が10だったのが10のままです。
ところが、オブジェクトの中に入っていた値xは20から21に増えています。
この違いはどこから出てきたのでしょうか。
ローカル変数にはスコープがあり、それぞれのメソッド内の変数は名前が同じでも別の変数です。
main( )とfoo( )両方のメソッドにaという変数とobjという変数があります。
それぞれは別物で、データは8行目のメソッド呼び出しで引数として渡されています。
この8行目がポイントです。
1つ目の引数aは、中身の値10を渡しています。
メソッドfoo( )ではその値を保存する変数aをこちらでも新しく作り、そこに10という値をコピーします。
一方、2つ目の引数objは同様にその中の値を渡しますが、これはあくまでも参照値です。
インスタンスまでは渡していません。
基本データ型の変数aは、メソッドに渡したときにaそのものを渡すのではなく、aの中身の10という値を知らせているだけです。
そして渡された値は変数に保存しなければ使えないので、メソッドfoo( )では新しく変数を作ります。
これは変数のコピーを作ったと考えられます。
一方オブジェクトの場合は、引数に参照値を渡します。
これは「インスタンスがどこにあるか」を教えてしまっているわけです。
コピーを作るのではなく大元のデータ(1つしかありませんが)のありかを教えてしまって、値を変更することが可能になっています。
ポイント!値渡しと参照渡し
このような違いを「値による呼び出し」・「参照による呼び出し」と言います。
Javaでは基本データ型の変数は値による呼び出しで、オブジェクトは参照による呼び出しです。
また、メソッドへ値を渡すことを「値渡し」、メソッドへ参照を渡すことを「参照渡し」とも呼びます。
- 値による呼び出し(Call by value)
変数などの中身の値を教えて、変数のコピーを作ります。
- 参照による呼び出し(Call by Reference)
データの参照を渡します。データのありかを教えてしまいます。
まとめ
- 参照型変数とは
インスタンスのある場所を保持するための型を参照型といい、その変数を参照型変数といいます。
- 参照型変数の特性
参照型変数をメソッドの引数として渡した場合、データが変更されてしまうことがあるので注意が必要です。
「参照型 Part2」へ続きます
独学で挫折しそうになったら、オンラインプログラミングスクール