未来エンジニア養成所Blog

プログラミングを皆に楽しんでもらうための情報をお届けします。

【Java】基本パッケージ Part2

title

前回に引き続き基本(java.lang)パッケージにあるStringクラスや、Objectクラスについて解説します。

前回の記事はこちら phoeducation.work


Stringクラスのインスタンス化

Stfingクラスのインスタンス化には2通りの方法があります。

インスタンス化の方法によってメモリ上のインスタンスの保管場所が異なります。

newキーワードを使ってインスタンス化した場合は、newされるたびに1つずつインスタンスがヒープ領域と呼ばれる標準メモリ領域に作成されます。

もう一つのインスタンス化の方法は、newキーワードを使わずに=(代入演算子)を使用して、文字列を代入する方法です。

この方法でインスタンス化するとメモリ領域内の文字列プールという場所に保管されます。

文字列プールに保管されたインスタンスは同じ文字列であった場合、メモリ節約のため一度だけ生成され、それを再利用するようになっています。


【Stringクラスのインスタンス化】

public class StringPool {
    public static void main(String[] args){
        String a1 = new String("AAA");
        String a2 = new String("AAA");
        String a3 = new String("AAA");

        System.out.println("a1 == a2 → " + (a1 == a2));
        System.out.println("a2 == a3 → " + (a2 == a3));
        System.out.println("a1 == a3 → " + (a1 == a3));

        String a4 = "AAA";
        String a5 = "AAA";
        String a6 = "AAA";

        System.out.println("a4 == a5 → " + (a4 == a5));
        System.out.println("a5 == a6 → " + (a5 == a6));
        System.out.println("a4 == a6 → " + (a4 == a6));
    }
}


実行結果

a1 == a2 → false
a2 == a3 → false
a1 == a3 → false
a4 == a5 → true
a5 == a6 → true
a4 == a6 → true


3行目~5行目はStringクラスのインスタンス化をnewキーワードで、11行目~13行目は代入演算子でインスタンス化し、それぞれに「AAA」という文字列を格納しています。

演算子==を使うと、左辺と右辺のオペランドが同じインスタンスであるかどうかを調べることができます。

newキーワードで作成されたインスタンスが格納されているa1、a2、a3はヒープ領域に別々に生成されます。

したがって、7行目~9行目の==演算子の結果は、別々のインスタンスなのですべてfalseが返されます。

一方、代入演算子でインスタンス化されたa4、a5、a6は文字列プールに格納され、同じAAAという文字列を使いまわします。

したがって、12行目~14行目の==演算子の結果は、同じインスタンスなので、すべてtrueが返されます。

メモリ領域


==演算子
==演算子は左辺のオペランドと右辺のオペランドが同じ値である場合trueを、異なる値の場合falseを返します。

(例)
System.out.println(10 == 10); → true
System.out.println(10 == 20); → false

また、==演算子はインスタンス(オブジェクト)の比較もできます。

(例)
Human taro = new Human(); Human jiro = new Human(); 
System.out.println(taro == jiro); → false

また、参照がないことを示すnullと比較をすると、falseが返されます。


Objectクラスのequals()メソッド

前節ではオブジェクトが同じものかどうかを調べるために、==演算子を使用しましたが、Objectクラスのequals()メソッドを使って調べることもできます。

Objectクラスはすべてのクラスのスーパークラス名なので、サブクラスでequals()メソッドを定義することなく使用できます。

戻り値の型、メソッド名、引数が明記されているメソッドの形をシグネチャと言います。

メソッドのシグネチャはjavadocのjava.langパッケージのObjectクラスからメソッドで調べる事ができます。


equals()メソッドのシグネチャを見ると、戻り値の型がbooleanであることがわかります。

これはオブジェクト同士を比較してtrueやfalseというboolean型の戻り値を返すという意味です。

また修飾子にstaticがついていません。

これはstaticメソッドではなく、インスタンスメソッドであることを意味します。

インスタンスメソッドはまずインスタンス化をしてインスタンス(オブジェクト)を作成してから使用するものでしたね。

使い方は「オブジェクト.equals(比較したいブジェクト)」と記述をします。


【Objectクラスのequals()メソッド】

public class EqualsSample1 {
    public static void main(String[] args) {
        MyObj obj1 = new MyObj();
        MyObj obj2 = new MyObj();
        MyObj obj3 = obj2;
        MyObj obj4 = null;

        System.out.println("obj1.equals(obj2):" + obj1.equals(obj2));
        System.out.println("obj2.equals(obj3):" + obj2.equals(obj3));
        System.out.println("obj1.equals(obj3):" + obj1.equals(obj3));
        System.out.println("obj1 == obj4:" + (obj1 == obj4));
        System.out.println("obj1.equals(obj4):" + obj1.equals(obj4));
        System.out.println("obj4.equals(obj1):" + obj4.equals(obj1));
    }
}


実行結果

obj1.equals(obj2):false
obj2.equals(obj3):true
obj1.equals(obj3):false
obj1 == obj4:false
obj1.equals(obj4):false
Exception in thread "main" java.lang.NullPointerException at EqualsSample1.main(EqualsSample1.java:13)


EqualsSample1クラスでは、MyObjのインスタンスを2つ作成し、変数obj1とobj2に格納しています。

変数obj3にはobj2を代入しているので、obj3はobj2と同じオブジェクトを参照しています。

変数obj4はnullを代入しているので、オブジェクトを何も参照していないという意味です。

オブジェクト参照


8行目のobj1.equals(obj2)では、obj1が参照しているオブジェクトはobj2が参照しているオブジェクト同じものかをequals()メソッドで調べています。

上図のようにobj1とobj2は別のオブジェクトを参照しているのでfalseが返されます。


9行目のobj2.equals(obj3)は、obj2が参照しているオブジェクトとobj3が参照しているオブジェクトが同じかどうかを調べています。

上図の通り2つは同じものを参照しているので、trueが返されます。


10行目のobj1.equals(obj3)は、obj1が参照しているオブジェクトはobj3が参照しているオブジェクト同じものかを調べています。

上図のようにobj1とobj3は別のオブジェクトを参照しているのでfalseが返されます。


11行目の(obj1 == obj4)は、==演算子でobj1とobj4を比較しています。

obj4は何も参照していないので、falseが返されます。


12行目のobj1.equals(obj4)は、obj1が参照しているオブジェクトはobj4が参照しているオブジェクト同じものかを調べていますが、obj4は何も参照していないのでfalseが返されます。


13行目のobj4.equals(obj1)は、obj4が参照しているオブジェクトはobj1が参照しているオブジェクト同じものかを調べています。

ここで注意が必要です。

obj4は何も参照していないのでnullが格納されています。

equals()メソッドではnullを何かと比較するとNullPointerExceptionという例外(エラー)が発生します。

例外については後述しますが、通常、例外が発生すると、特別な処理を行わない限りは、そこでプログラムが終了します。

実行結果を見ると13行目で例外が発生したことがわかります。

12行目も13行目と同様に、obj4を比較の対象にしていますが、12行目では、参照のあるobj3が何かと比較されているのであり、それがたまたまnullであっただけで、問題ありません。13行目ではnullであるobj4を扱えないという意味で例外が発生しています。


Stringクラスのequals()メソッド

Objectクラスのequals()メソッドは同じオブジェクトであるかを比較しますが、ObjectクラスのサブクラスであるStringクラスのequals()メソッドはオーバーライドされており、同じ文字列かどうかで比較をします。


【Stringクラスのequals()メソッド】

public class EqualsSample2 {
    public static void main(String[] args) {
        String s1 = new String("Apple");
        String s2 = new String("Apple");
        String s3 = "Apple";
        String s4 = "Orange";

        if(s1 == s2){
             System.out.println("s1とs2は同じオブジェクトです");
        }else{
             System.out.println("s1とs2は異なるオブジェクトです");
        }
        if(s1.equals(s2)){
            System.out.println("s1とs2は同じ文字列です");
        }else{
            System.out.println("s1とs2は異なる文字列です");
        }

        if(s1 == s3){
            System.out.println("s1とs3は同じオブジェクトです");
        }else{
            System.out.println("s1とs3は異なるオブジェクトです");
        }
        if(s1.equals(s3)){
            System.out.println("s1とs3は同じ文字列です");
        }else{
            System.out.println("s1とs3は異なる文字列です");
        }

        if(s1 == s4){
            System.out.println("s1とs4は同じオブジェクトです");
        }else{
            System.out.println("s1とs4は異なるオブジェクトです");
        }
        if(s1.equals(s4)){
            System.out.println("s1とs4は同じ文字列です");
        }else{
            System.out.println("s1とs4は異なる文字列です");
        }
    }
}


実行結果

s1とs2は異なる文字列です
s1とs2は同じ文字列です
s1とs3は異なるオブジェクトです
s1とs3は同じ文字列です
s1とs4は異なるオブジェクトです
s1とs4は異なる文字列です


EqualsSample2クラスでは、変数s1とs2にnewキーワードを使って文字列Appleを格納したオブジェクトをヒープ領域に生成しています。

また変数s3では代入演算子を使って文字列Appleを格納したオブジェクトを文字列プールに生成しています。

同様に、変数s4は代入演算子を使って文字列Orangeを格納したオブジェクトを文字列プールに生成しています。(下図参照)

文字列プール


8行目~12行目では、==演算子を使ってs1とs2を比較しています。

==演算子は同じオブジェクトかどうかを調べるので、falseがかえり、if文の結果は「異なるオブジェクトです」と出力されます。


13行目~17行目では、Stringクラスのequals()メソッドを使ってs1とs2を比較しています。

Stringクラスのequals()メソッドは格納されている文字列が同じであればtrueを返すので、if文の結果は「同じ文字列です」と出力されます。


19行目~23行目では、==演算子を使ってs1とs3を比較しています。

s1とs3は上図のように格納されている領域が違う別のオブジェクトなのでfalseが返され「異なるオブジェクトです」と出力されます。


24行目~28行目では、equals()メソッドを使ってs1とs3を比較しています。

両方ともAppleという文字列を持っているので、trueがかえり「同じ文字列です」と出力されます。


30行目~34行目では、==演算子を使ってs1とs4を比較しています。

s1とs4は異なるオブジェクトなので、falseがかえり「異なるオブジェクトです」と出力されます。


35行目~39行目では、equals()メソッドを使ってs1とs4を比較しています。s1はApple、s4はOrangeという文字列を持っているので、falseがかえり「異なる文字列です」と出力されます。


同じ文字列かどうかを検査するには
同じ文字列かどうかを調べるには、Stringクラスのequals()メソッドを使用します。

==演算子では文字列の比較はできないので、注意しましょう。


まとめ

  • java.langパッケージとObjectクラス

    • java.langパッケージに含まれるクラスは、importしなくても利用できます。
    • Objectクラスは、すべてのクラスのスーパークラスです。
  • Stringクラス
    Stringクラスのオブジェクトはイミュータブル(不変)です。

  • StringBufferクラス
    StringBufferクラスのオブジェクトはミュータブル(可変)です。

  • Stringクラスのインスタンス化

    • newキーワードでインスタンス化すると、メモリ上のヒープ領域に生成されます。
    • 代入演算子でインスタンス化すると、メモリ上の文字列プールに生成されます。
  • Objectクラスのequals()メソッド

    • 同じオブジェクトであるかを調べます。
    • nullを比較すると、NullPointerException例外が発生すます。
  • Stringクラスのequals()メソッド
    同じ文字列であるかを調べます。


参考図書



独学で挫折しそうになったら、オンラインプログラミングスクール
未来エンジニア養成所Logo



あわせて学習したい

phoeducation.work