いつか、どこかで

【Java】文字列比較に==演算子を使ってはいけない理由

研修のときに、口を酸っぱくして説明するんだけどみんな忘れちゃうやつ。

String型は参照型である

というのはどういうことかというと、

String str = "ABC";

と宣言したとき、メモリ上は、 f:id:riko111:20200312185229p:plain

となるのだけれど(イメージです。実際のメモリ配置を表してはいません)、これが例えば

public class Test {
    public static void main(String[] args){
        String str = "ABC";
        if(str == args[0]){
            System.out.println("同じ文字列です");
        } else {
            System.out.println("違う文字列です");
        }
    }
}

というコードのとき、コンパイルして実行したら、

>java Test ABC
違う文字列です

となる。これはメモリ上、 f:id:riko111:20200312185232p:plain

こういう状態で、●と□を比較しているので、等しくないと判断される。

public class Test {
    public static void main(String[] args){
        String str = "ABC";
        if(str.equals(args[0])){
            System.out.println("同じ文字列です");
        } else {
            System.out.println("違う文字列です");
        }
    }
}

と記述すれば、strが参照している文字列と、args[0]が参照している文字列の比較に鳴るので、等しいと判断される。

メモリ管理のやっかいなところ

で、問題は、

public class Test {
    public static void main(String[] args){
        String str1 = "ABC";
        String str2 = "ABC";
        if(str == str2){
            System.out.println("同じ文字列です");
        } else {
            System.out.println("違う文字列です");
        }
    }
}

と書いたら、等しいと判断されること。

これはなぜかというと、

  1. String型はクラスで定義された型なので、正確には
String str1 = new String("ABC");

と書くべきである。 でもStringはとってもよく使うので例外的に、

String str1 = "ABC";

と書ける。

2.ただし、省略形の書き方は、明示的なオブジェクトの生成を指定していないので、

String str1 = "ABC";
String str2 = "ABC";

と書くと、str1はオブジェクト生成が行われるが、str2では先程生成したオブジェクトをそのまま参照する。 つまり、メモリ上は f:id:riko111:20200312190359p:plain

になる。 これはJVMがそういうふうにメモリ管理することにしてるので。

なので最後のコード書いて、なーーんだ==で文字列比較してちゃんと出来るじゃ~んってならないでね。 そして、C++C#だと、==で文字列比較できるのにー、っていう人、それはC++C#には演算子オーバーロードという機能があって、Javaにはないからです…