Javaの文字列リテラルとそのインスタンス
Javaで、同じ内容の文字列リテラルで初期化されたString型のクラスは、同じインスタンスになるようだ。 表現が下手なせいか、文章だと何を言っているかわからないけども、例えば以下のコード。
String str1 = "Hello World!"; String str2 = "Hello World!"; String str3 = "Hello " + "World!";
上記では、String型の変数str1~str3が宣言されおり、初期値が文字列リテラルで指定されている。
初期値はすべて"Hello World!"
だ。
このような場合、str1~str3は、同じインスタンスを指している。
以下が、その内容を確認するためのサンプルコードとなる。
サンプルコード
String str1 = "Hello World!"; String str2 = "Hello World!"; String str3 = "Hello " + "World!"; String str4 = new String("Hello World!"); String str5 = new String(str1); // equalsメソッドで、クラスの中身(文字列)を比較 System.out.println("str1.equals(str2) is " + str1.equals(str2)); // 文字列リテラル同士 System.out.println("str1.equals(str3) is " + str1.equals(str3)); // 文字列リテラル同士 System.out.println("str1.equals(str4) is " + str1.equals(str4)); // 文字列リテラルとnewしたもの。 System.out.println("str1.equals(str5) is " + str1.equals(str5)); // 文字列リテラルとnewしたもの。 // "=="でインスタンス自体を比較 System.out.println("str1 == str2 is " + (str1==str2)); // 文字列リテラル同士 System.out.println("str1 == str3 is " + (str1==str3)); // 文字列リテラル同士 System.out.println("str1 == str4 is " + (str1==str4)); // 文字列リテラルとnewしたもの。 System.out.println("str1 == str5 is " + (str1==str5)); // 文字列リテラルとnewしたもの。
サンプルコードでは、String型の変数が5つ宣言されている。str1~str3が、文字列リテラルで初期値を指定したもの。str4とstr5がnewで新しくString型のインスタンスを生成したものである。
実行結果
str1.equals(str2) is true str1.equals(str3) is true str1.equals(str4) is true str1.equals(str5) is true str1 == str2 is true str1 == str3 is true str1 == str4 is false str1 == str5 is false
上記実行結果のとおり、equalsメソッドで文字列の比較を行った場合、str1~str5のすべてが"Hello World!"
を保持しているため、結果はすべてtrueとなっている。
しかし、==
でインスタンスを比較した場合は、文字列リテラル同士ではtrueとなっており、 文字列リテラルとnewしたものを比較した場合はfalseになっている。
この結果から、同じ内容の文字列リテラルは、同じインスタンスを指していることがわかる。
言語仕様
ちなみにこの動作はJavaの言語仕様として決められているようだ。
A string literal is a reference to an instance of class String (§4.3.1, §4.3.3). Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.
The Java Language Specification, Java SE 8 Edition - 3.10.5. String Literals
ざっくりというと、文字列リテラルは、String型のインスタンスへの参照を示すよ。 文字列リテラル(と定数式の結果の文字列)は、String型のinternメソッドを使って、ユニークなインスタンスを使うようにしてるよ。……と書いてあるみたい。
余談
ちなみにこんな時に注意。
独自クラス(String型のフィールドを持つ)のequalsメソッドの単体テストを書く場合、以下のようにしてしまうと、String型のフィールドが==
で比較されていた、みたいな誤りが検出できないぞ。
@Test public void testEquals() throws IOException { Hoge obj1 = new Hoge("dummy string"); Hoge obj2 = new Hoge("dummy string"); boolean actual1 = obj1.equals(obj2); assertThat(actual1, is(true)); boolean actual2 = obj2.equals(obj1); assertThat(actual2, is(true)); }
関連リンク
- The Java Language Specification, Java SE 8 Edition - 3.10.5. String Literals
- Java(tm) Platform, Standard Edition 8 API仕様 - String#inter()
(おわり)