概要
JavaのStringの挙動について整理してみた。
同一性と同値性
String
型のオブジェクトの同一性と同値性(等価性)について、以下のコードで確認した。
s2
はs1
の参照値を代入しているので、s1
とs2
は同一であり同値- その後に
s2
の内容を変更すると、s1
とは内容も参照値も異なるようになる s3
には別のリテラルでs1
と同じ内容を代入しているが、s1
とs3
は同一であり同値
すなわち同じ内容のString
リテラルを用いると、それらリテラルは1つのインスタンスとして共有される。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
String s1 = "ABC"; String s2 = s1; System.out.println(s1 == s2); // true System.out.println(s1.equals(s2)); // true s2 += "DEF"; System.out.println(s1 + ", " + s2); // ABC, ABCDEF System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // false String s3 = "ABC"; System.out.println(s1 == s3); // true System.out.println(s1.equals(s3)); // true |
同値なオブジェクトと同一性
以下のコードで、同値なオブジェクトが常に同一になるか(共有されるか)確認した。
s5
は2つのString
リテラルからs4
と同じ値のリテラルを作り出していて、s4
とs5
は同一であり同値s6
は内容が"ABC"
の変数s1
とString
リテラルからs4
と同じ値のリテラルを作り出しているが、s4
とs6
は同値ではあるが同一ではない
すなわち以下の様に整理される。
- リテラルから生成された内容が同じ場合は1つのインスタンスとして共有される
- 変数を含めた操作で生成されたインスタンスは、同じ内容のものがあっても別のインスタンスとして扱われる
1 2 3 4 5 6 7 8 9 |
String s4 = "ABCDEF"; String s5 = "ABC" + "DEF"; System.out.println(s4 == s5); // true System.out.println(s4.equals(s5)); // true String s6 = s1 + "DEF"; System.out.println(s4 == s6); // false System.out.println(s4.equals(s6)); // true |
Stringのイミュータビリティー
以下の様にString
はイミュータブルと言える。
- 異なる内容の
String
リテラルは異なるインスタンスとして生成され、参照される String
クラスで変更を伴うメソッドはString
を戻り値としており、インスタンスの内容を直接は変更できないString
クラスはfinal
宣言されており継承はできない(クラスを継承して自身を変更することはない)
なおString
に関しては、同値であっても同一でない(インスタンスが異なる)場合がある。
- リテラル同士の結合結果が他のリテラルに等しい場合、その内容は1つのインスタンスとして共有される(同一であり同値)
String
変数を含む結合結果が他のリテラルに等しい場合でも、それらは別のインスタンスとして扱われる(同値だが同一ではない)
リテラルと変数での挙動の違いは、コンパイルレベルで値を同じかどうかを判断しているためか。
これらの場合があるとしても、一度設定されたオブジェクトの内容を変更することはできないため、String
型はイミュータブルであると言える。