前節までに見てきたとおり、 Java の基本データ型には「文字列」が含まれていません。本節では、文字列を扱うための基本的な知識を紹介します。
単一文字は基本データ型の char
型で表され、char 型で定義した変数に保持できます。そして、複数文字のシークエンスである文字列(ストリング)を変数の値に保持するには、基本データ型ではなく、String 型のオブジェクトとして保持します。
まだオブジェクトの説明をしていないので、ここでは文字列操作には触れません。文字列操作の詳細は String クラスと StringBuffer クラスを参照してください。とりあえず、次の例を見てください:
StringDemo.java
:
class StringDemo {
public static void main(String[] args) {
String str = "こんにちは";
System.out.print("挨拶:" + str);
}
}
三行目で、 String
型変数 str
に文字列 こんにちは
を代入しています。書式は、基本データ型の変数宣言/初期化と同じですね。
実行例:
C:\java>javac StringDemo.java C:\java>java StringDemo 挨拶:こんにちは C:\java>
正しく言うと、 String
というのはクラス名であって、インスタンスを参照する変数 str
に、インスタンス こんにちは
を参照するIDが代入される事になります。しかし、ここでは深く追求しません。詳細は、インスタンス化の説明の項に譲ります。ここでは書式だけ覚えてください。
すると気になるのは、 main()
メソッドのカッコ内に現れている String[] args
です。実はこれも、文字列型変数(String
クラスのインスタンスの参照変数) の定義にほかなりません。但し、 args
は普通の文字列の参照ではなく、複数の文字列の塊の参照です。このような複数の値の塊のことを配列と呼びます。詳しくは配列の項で説明します。
あとで詳しく触れますが、変数は基本データ型と参照型の二つに分けられます。それぞれで、比較方法と初期化の規則が異なります。文字列はString型という参照型です。
例えば、int a = 1
とすれば、a には具体的な値である 1 が代入され、以降の行では a
は 1
と全く等価に扱えます。参照型の場合は、具体的な値は変数とは別の領域に存在し、変数にはその値を参照する ID が代入されます。
変数を別の変数に代入すると、もとの変数に代入されている値が別の変数にコピーされます。
基本データ型変数の場合は、もとの変数のメモリ領域に保持されている値が、代入先の変数のメモリ領域に書き込まれます。例えば、もとの変数のメモリ領域に1が書き込まれている場合、代入先変数のメモリ領域にも1が書き込まれ、1が都合二箇所に書き込まれることになります。
参照型変数の場合は、もとの変数のメモリ領域には、別の領域に存在する値の実体を指し示す ID が書き込まれており、代入先の変数のメモリ領域に書き込まれるのもこの ID になります。従って、ID は二箇所に存在する事になりますが、実体は一つだけです。
基本データ型(プリミティブ型)の変数は、当該変数名でメモリ上に領域が確保され、具体的なリテラルが代入されています。したがって、二つの変数に代入されたリテラル同士を比較しようとすれば、変数を比較しても同じことですから、次の条件式は必ず真です。
int var1=100, var2=100; System.out.println(var1 == var2); //true
==
演算子は、変数に代入されたリテラル同士の比較演算子です。
また、基本データ型の変数は、基本的には明示的な代入によって初期化が必要です。明示的な代入を怠ったときに暗黙に代入されるようなデフォルト値は存在しません。但し、後で説明するメンバー変数(フィールド)に基本データ型を定義する場合は、デフォルト値が存在し、明示的な代入をしなくてもコンパイルエラーを起こしません。
参照型の場合は、当該変数名で確保されたメモリ上の領域には、別の領域に存在する実体へのポインタ(識別する為の ID)が代入されています。変数名とその値はスタックと呼ばれる領域に書き込まれ、変数値が参照する実体はヒープと呼ばれる領域に書き込まれます。
後で詳しく紹介しますが、参照型変数の場合は、省略時の値(デフォルト値)が定義されており、明示的な代入が無くてもコンパイルをとおります。このデフォルト値は null
であり、何も参照していないことを指します。 String
型変数は参照型変数なので、明示的な代入が行われていなければ、 null
値を保持しているはずです。
==
演算子は、変数の値を比較して、ブーリアン型の値(true/false)を返します。参照型変数の場合は、変数値は実体を参照するIDなので、それが異なる領域に保持されたデータを参照している場合は偽を返します。例えば、文字列 "Hello" が、メモリ上で別の実体として二箇所に存在していれば、 ==
演算子でそれらの変数を比較すれば、評価結果は偽になります。
![]() |
図. オブジェクトの == と equals() による比較 |
---|
オブジェクトの実体であるインスタンスの等価性を比較するときは、メソッド equals()
を用います。使い方は次のように記述します。
// String型変数str1とstr2を比較 str1.equals(str2); // 次のようにも書ける str.equals("Hello"); // 次のようにも書ける "Hello".equals(str);
文字列に対して equals()
を使うと、文字列の一文字ずつを char
型で取り出して、各々を ==
で比較するため、別のオブジェクトであっても、同じ文字列であれば true
を返してくれます。
文字列はクラス java.lang.String
型のれっきとしたオブジェクトなのですが、頻繁に使うので、プリミティブ型と同様の記法も許されており、その場合は、equals()
と ==
が、ある程度までは、同じ結果を返すような仕組みを持っています。
オブジェクトは、一般的には次の書式で生成(インスタンス化)します。
// 一般論 クラス名 変数名 = new クラス名(引数); // String型の場合 String str = new String("文字列");
文字列は頻繁に使うために特例措置がなされていて、次のように二重引用符で文字列をくくると、自動的に String 型オブジェクトを生成してくれます。
String str = "文字列";
この書式が通常のオブジェクト生成と異なるのは、ここで記述した文字列と等価な String 型オブジェクトが既に存在していれば、既存の String 型オブジェクトを参照する ID を代入するということです。内部的には次の手順が踏まれています。
文字列について、擬似プリミティブ型と、通常のインスタンス化の違いをハイライトするサンプルを紹介します。
StringTest.java
:
class StringTest { public static void main(String[] args) { //擬似プリミティブ型 String str1 = "Hello"; String str2 = "Hello"; System.out.println("str1 == str2: " + (str1 == str2)); //オブジェクト String obj1 = new String("Hello"); String obj2 = new String("Hello"); System.out.println("obj1 == obj2: " + (obj1 == obj2)); System.out.println("obj1.equals(obj2): " + (obj1.equals(obj2))); } }
実行例:
C:\java>javac StringTest.java C:\java>java StringTest str1 == str2: true obj1 == obj2: false obj1.equals(obj2): true
擬似プリミティブ型で作った文字列オブジェクト str1
/str2
の場合は、プリミティブ型と同様の意味で ==
で比較できます。これはオブジェクトとしては特殊なことです。文字列は頻繁に使うために、プリミティブ型と同様の操作性を確保するために採用された特例措置によるものです。
通常のオブジェクトの作成と同じ形式で作った文字列オブジェクト obj1
/obj2
の場合は、 ==
では、保持している値自身が比較されるので、別のオブジェクトの参照同士を比較したことになり、 false
が戻ります。 equals()
では、参照しているオブジェクトの実体が比較されるので、今の場合は true
が戻ります。