見出し画像

【JavaSilver #01】String.intern()メソッド から色々学びすぎてしまった...

勉強中の内容を文章におこすことで、自分の知識にしようという目論見(第一回)
Java SE 11 Silver 資格の取得を目指しています。
勉強目的というよりは、暇つぶし感覚のコラムみたいな気分で読むといいかも?

今日の問題はこれ

public class Main {
    public static void main(String[] args) {
        String a = "test";
        String b = new String(a);

        // カウンタ変数
        int cnt = 0;
        if (a.intern() == "test") {
            cnt++:
        }
        if (b.intern() == "test") {
            cnt++:
        }
        if (a.intern() == b.intern()) {
            cnt++:
        }

        return cnt;
    }
}

『返ってくる変数 cnt の値はいくつか?』

この問題、ただ「intern()」メソッドの知識を問うものかと思いきや、たった一問を理解しようとするだけで、いろいろな事柄を理解できた(気がする)すごい問題。

まず、String.intern() メソッドがなにかを知らないと話にならないので、調べてみると、
『コンスタントプールを含むメモリ内の文字列から、同じ文字列を探して再利用するメソッド』らしい。そして、『見つからなかった場合は同じ文字列を生成する』らしい。
なるほどわからん。

前提知識1 同値性と同一性

『同値性』――値がいっしょ。メモリの参照位置はきにしない。
『同一性』――メモリの参照位置がいっしょ。値がいっしょでも、メモリの参照位置が違ったらダメ

文字列と文字列を比較するときの方法は、とりあえず2つ。
・str1.equals(str2)
・str1 == str2
equals() での比較は、同値性で
== 演算子での比較は、同一性で 比較する。

先輩プログラマーが、口を酸っぱくして「equals()」で比較しろと言うのは、これが理由なんですね。とりあえず使っとけで、内部事情までは理解してませんでした。

そしてこれが、次項の前提知識2に繋がります。

前提知識2 文字列変数の保存領域

まず、コンスタントプールとは、定数とかを保存する領域らしい。
String str = "aaa" ←こうやって生成するときは、コンスタントプールに保存される。
String str = new String("aaa") ←インスタンス化で生成した場合は、ヒープ領域(外から見えない?)に保存されるらしい。

らしいばっかで、すみませんホント。全然細かいところ気にしてコーディングしてなかったので。(こういう細かなところを知るために資格とろうと勉強してる)

コンスタントプールに文字列が保存されていた場合に、同じ文字列をまた生成すると、コンスタントプールから持ってきて、同じメモリの位置を参照してくれる。
私みたいに、無知なプログラマーが実装しても、メモリ効率を良くしてくれているっぽい。やさしい。
ヒープ領域だと、見れないからかどうか知らないけど、参照しないで同じ文字列を別のメモリ領域に再生成する。

なので、
String str1 = "aaa";
String str2 = "aaa";
System.out.print(str1 == str2) は、true を返す。(同一性。参照箇所がいっしょ)

String str1 = new String("aaa");
String str2 = new String("aaa");
System.out.print(str1 == str2) は、false を返す。

本題

public class Main {
    public static void main(String[] args) {
        String a = "test";
        String b = new String(a);

        // カウンタ変数
        int cnt = 0;
        if (a.intern() == "test") {
            cnt++:
        }
        if (b.intern() == "test") {
            cnt++:
        }
        if (a.intern() == b.intern()) {
            cnt++:
        }

        return cnt;
    }
}

① if (a.intern() == "test") {
String a = "test"; で、コンスタントプールに "test" を生成している。
a.intern() は、コンスタントプールから持ってきてはいるが、結局は自分自身を返している?
右辺 "test" も、コンスタントプールから生成しているので、参照はいっしょ。
「==」は同一性を判定しているので、cntに1加算。

② if (b.intern() == "test") {
String b = new String(a); で、新たに文字列をヒープ領域に作成している。
なので、この状態で a == b を比較すると、falseが返ってくる。(参照場所がちがう)
だが、b.intern() をすることで、
『コンスタントプールから"test"を探して、見つかったらその参照を上書きする』。つまり、変数bの参照先が、変数aと同じになる。
なので、結果はtrue、cntに1加算。

なるほど。intern()メソッドの意味、そういうことね。

③ if (a.intern() == b.intern()) {
いわずもがな、どちらもコンスタントプールから参照を上書き。
同じ参照を見ているので、結果はtrue、cntに1加算。

こたえ:cnt = 3

このメソッド自体、初見だったのですが、これについて調べていく過程で学んだことが多すぎました。

中途半端な知識のまま書きなぐったので、間違いなどあれば、指摘いただけると幸いです。

この記事が気に入ったらサポートをしてみませんか?