Revised: Oct./4th/2004: Since: Dec./30th/2001
オブジェクトを参照するクラス型変数は、基本データ型の変数に対して、参照型変数と呼ばれます。配列の変数も参照型変数です。これらの変数に共通することは、型宣言と初期化のほかに、生成という段階が存在することです。
int i; //宣言 i = 10; //初期化
変数の型を宣言して、値を明示的に代入しています。ローカル変数の場合は明示的に代入しないと使えません。フィールドの場合は、型宣言すれば、型に対応したデフォルト値が自動的に代入されます。
基本データ型の変数は、具体的な値を保持します。基本データ型変数同士の代入は、その変数が保持している値のコピーが行われます。例えば、基本データ型変数に5を代入し、この変数を別の基本データ型変数に代入すれば、メモリ上には二箇所に5が書き込まれて保持されます。
int[] array; //宣言
array = new int[4]; //生成
array[0]=10; array[1]=11; array[2]=12; array[3]=13; //初期化
実は、配列も一つのオブジェクトであり、各種のメソッドやフィールドが使えます。これらの殆どは java.lang.Object
クラスで定義されたものです。明示的な代入が行われていない場合は、 null
で初期化されます。
配列型変数は参照型変数なので、複数の要素の連なりを保持しているわけではなく、その参照(識別する為の ID)が保持されています。配列型変数を別の配列型の変数に代入しても、メモリ上の実体がコピーされるわけではありません。右辺から左辺に、識別する ID がコピーされるだけです。
new
演算子が、右辺で生成された配列を参照するIDを返しています。
Date obj; //宣言
obj = new Date(); //生成
//初期化方法はクラスによりますね。
配列型とクラス型の参照型変数の場合は、生成 (construct) の段階で new
演算子を使っています。この演算子は、右辺で生成されたオブジェクトへの参照 (reference) を返します。上の例では、この参照は代入演算子 =
によって、左辺で定義されているクラス型変数 obj
に代入されています。オブジェクトが明示的に代入されていなければ、クラス型変数は null
で初期化されます。
参照型変数に保持されるのは、生成された実体への参照であり、オブジェクトの実体はメモリ上の別の場所に存在しています。変数名で確保されたメモリ領域には、実体の参照(実体を識別する為の ID)が保持されています。
参照型変数を別の参照型変数に代入すると、左辺の変数には、右辺の変数が参照している実体ではなく、参照(実体の ID)がコピーされます。従って、実体はメモリ上に一つだけであり、その実体を参照する変数が二つできたことになります。
従って、代入する参照型変数と、代入される参照型変数があるとき、後者の参照するオブジェクトを操作すると、前者の参照するオブジェクトも操作されたことになり、その逆も成り立ちます。参照型変数を代入すれば、同じ実体を参照する ID がコピーされて、同じ実体を参照することになるわけです。
new
演算子が、右辺で生成されたオブジェクトを参照するIDを返しています。
基本データ型の場合は、比較演算子 ==
で比較できます。
int i = 10, j = 10; i == j; //true
オブジェクトを比較する場合は、そのオブジェクトを参照している変数同士を、メソッド equals()
を使って比較します。
obj1.equals(obj2);
==
で比較すると、変数が保持している値が比較されます。参照型変数が保持しているのは実体への参照ですから、参照型変数同士を ==
で比較すると、実体を識別する ID が比較されることになります。別々のオブジェクトの実体を参照する ID は別のものですから、オブジェクトが等価であるかどうかには関係なく、必ず false
が返ってきます。
別々のオブジェクトが実質的に同じかどうか比較する場合は、 equals()
メソッドを使いいます。この場合は、例えば、 String
型のオブジェクトならば、同じ文字列を保持していれば true
です。 Date
型のオブジェクトならば、同じ日付を保持していれば true
です。
Date date1 = new Date(); Date date2 = new Date(); date1 == date2; //false date1.equals(date2); //true
equals()
メソッドは、 java.lang.Object
クラスで定義されていますが、各々のサブクラスでの比較の目的に応じてオーバーライドされています。
Equals.java
:
//Date クラスのインポート import java.util.Date; class Equals { public static void main(String[] args) { Date date1 = new Date(); Date date2 = new Date(); System.out.println("date1: " + date1); System.out.println("date2: " + date2); boolean bln1 = (date1 == date2); //false boolean bln2 = date1.equals(date2); //true System.out.println(" ==: " + bln1); System.out.println("equals(): " + bln2); } }
ここでは標準クラスライブラリの Date
クラスをインスタンス化しています。このコードの2行目のように、利用するためにはパッケージも明示した完全限定名をインポートするのが普通です。
このようにして二つのオブジェクトを生成し、それぞれを Date
クラス型変数から参照しています。これらは全く同じ条件で作ったオブジェクトですが、別のものです。変数に代入されている参照は別のものを指していますので、 ==
で比較すると false
です。しかし、同じ条件で作っているので、等価ですから、 equals()
メソッドは true
を返します。
C:\Java>javac Equals.java C:\Java>java Equals date1: Sun Mar 10 00:56:26 JST 2002 date2: Sun Mar 10 00:56:26 JST 2002 ==: false equals(): true
基本データ型の変数の場合は、フィールド以外のローカル変数の場合は、明示的に値を代入しておく必要があります。ローカル変数で明示的に代入していないとコンパイルを通りません。
一方、オブジェクト、配列を参照する参照型変数の場合は、フィールドであっても省略時の値(デフォルト値)が定義されており、生成と同時に代入されています。この値は null
です。これは何も参照していないことを明示する値です。