JavaSilver_躓いた問題をまとめてみた① コンスタントプール

f:id:nowa0402:20210514162016p:plain

こんにちは。のわです。
今回からしばらくの間、現在勉強中のJavaSilverから躓いた問題をまとめてみたいと思います。
第一回目はコンスタントプールです。



早速ですが、問題です。

  String a = "abc";
  String b = "abc";
  System.out.println(a == b);

上記コードを実行時、出力結果は何になるでしょうか。





答えは【true】です。
私は【false】だと思っていました。


では下記コードだとどうなるでしょうか。

  String a = "abc";
  String b = new String("abc");
  System.out.println(a == b);





答えは【false】です。
この違いはなんでしょうか。




解説です。

なぜ私が最初のコードの実行結果を【false】だと思っていたのか。
それは『String 変数 = "文字列";』を、都度Stringインスタンスが生成されているものだと勘違いしていたからです。

私の脳内イメージは
「Stringでaとbを別々に生成されたインスタンス
「a == bは同じインスタンスかどうかみているから答えはfalse」

となっていました。

通常の考え方であれば上記の認識で間違いはありません。
ただ、Stringは違っていたのです。
では、Stringインスタンス生成時の特別ルールとはなにか。
キーワードは『コンスタントプール』になります。

コンスタントプールは
『コード中に同じ文字列リテラルが登場した場合、Stringインスタンスへの参照先が使いまわしされる仕組み』
になります。

言い換えるなら
『同じ言葉で格納されたStringインスタンスは同じ参照先をみている=同一インスタンス
となります。

なぜコンスタントプールが存在するのか
文字列リテラルはプログラム上にたくさんでてきます。
そのため、同じ文字列を何度も生成するのはメモリをたくさん使う羽目になります。

上記理由から
「同じ文字列だったら、できる限り使いまわししてメモリ処理を軽減させようね」という目的で存在しているわけです。


では、もう一度最初のコードをみてみましょう。

  String a = "abc";
  String b = "abc";
  System.out.println(a == b);

この問題では同文字列リテラルインスタンスを2つ生成しています。
上記解説に当てはめるなら『String b はString aと一緒の文字列だから、参照先を一緒にしている』状態です。
そのため、最後の同一判定でtrueとなるわけです。



そうすると違う疑問が出てきます。
なぜ、2つ目のコードでは【false】になるのでしょうか

  String a = "abc";
  String b = new String("abc");
  System.out.println(a == b);


この場合、String bはnew演算子インスタンスを生成しています。
new演算子で明示的にインスタンスを生成すると
コンスタントプールを参照せずに、インスタンスを生成することができます。

つまり、String a でabcの文字列リテラルはコンスタントプール内に出来ているのですが
String bではコンスタントプールを参照しないで生成している状態です。

上記理由からSting a とString b は参照先が異なるインスタンスになるため【false】になります。





では、次に先程のコードにあるメソッドを追記します。
この場合、答えはどうなるでしょうか。

  String a = "abc";
  String b = new String("abc");
  System.out.println(a == b.intern());


この場合、答えは【true】になります。
intern()メソッドは『コンスタントプールを含むメモリ内の文字列を探して再利用する』メソッドです。
因存在しない場合は、文字列をコンスタントプールに登録したのち、その登録先を参照先として格納します。


このことから、b.intern()で何が行われているか
b,intern()によって
『bに格納されている文字列をコンスタントプール含むメモリ内から検索し、あればその参照先を戻り値とする』
という処理が行われています。

この場合の文字列は「abc」
「abc」はString aが先にコンスタントプールに登録しています。
そのため、b.intern()にはString aと同じ参照先が格納されるため同一参照先をみることになります。
結果
『a == bは異なる参照先だから【false】』
『a == b.intern()はbの参照先がaと同じになったので【true】』

になります。



今回のまとめです。

 ◇コンスタントプールとは『文字列を保存し、同文字列出現時、使い回しできるようにする仕組みのこと』
 
 ◇Stringインスタンス生成時の右辺への記載を
  ・文字列だけにする⇒コンスタントプールの対象
  ・new演算子で明示的にする⇒コンスタントプールの対象外
 
 ◇intern()メソッドの機能『コンスタントプールを含むメモリ内の文字列を探す』
  ・あればその文字列の参照先が戻り値となる
  ・なければ文字列をコンスタントプールに登録した後、そこを参照先して戻り値を返す



今回も最後まで読んでいただきありがとうございました!