技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

Java:リテラル用の領域の確保について

Javaの変数の型は、プリミティブ型と参照型に大別されます。
そして、変数の値が同じであるかどうかを確認する場合、プリミティブ型は == で同じ値であることを確認できる(同じ値の場合はTrueになる)のに対し、参照型の場合は原則として == では確認できず、equalsメソッドを使う必要があります。
 
しかし、参照型の場合であっても、== で確認できる場合があります。
それは、参照型の変数にリテラルソースコード内に直接記述された定数値)を直接代入した場合です。
 
以下で、順番を追って説明していきます。
 
【メモリへの値の保持の方法】
変数の値はメモリに保持されます。
メモリは1バイト(8ビット)ずつ細かく区分けされており、それぞれの区分けについて「アドレス」と呼ばれる値により一意に場所を特定します。
(アドレスのバイト数は、64ビットOSの場合は8バイトです)
 
プリミティブ型も参照型も、「メモリに保持される」ということは変わりませんが、メモリに保持する値は変わります。プリミティブ型変数では値そのものをメモリに格納するのに対し、参照型変数ではメモリ上にその変数用の保存領域を確保した上で、その保存領域の場所を指し示すアドレスを変数の領域に格納します。
 
例として、以下のソースコードについて考えます。
import java.util.*;

public class Main {
    public static void main(String args) throws Exception {
        
    // プリミティブ型変数の例
    int i1 = 1;
    
    // 参照型変数の例
    int
il1 = new int {1,2,3};
    }
}
 
このソースコードの場合のメモリのイメージは以下です。
プリミティブ型変数の「i1」と、参照型変数の「il1」では、値の保持の方法が異なります。

 
【リテラル用の領域の確保】
リテラルの場合は、リテラル用の領域が別途確保されます。
そして、同じ値のリテラル値が複数の箇所で記述されていたとしても、全ての箇所において同じ領域が使いまわされます。
 
参照型変数でインスタンス化した(newした)場合は、仮に同じ値だとしても、インスタンス化する度に異なる領域が確保されるため、その挙動とは異なるものとなります。
 
例として、以下のソースコードについて考えます。
import java.util.*;

public class Main {
    public static void main(String args) throws Exception {
        
        // リテラルのアドレスを代入
        String s1 = "hoge";
        String s2 = "hoge";
    
        // 新たに領域を確保し、そのアドレスを代入
        String s3 = new String("hoge");
        String s4 = new String("hoge");

        // 他のリテラルのアドレスを代入
        String s2 = "fuga";
    }
}
 
このソースコードの場合のメモリのイメージは以下です。
リテラル値を代入した「s1」「s2」と、インスタンス化で新たに領域を確保した「s3」「s4」では、値の保持の方法が異なります。

 
【比較を行った場合の挙動の違い】
上記より、リテラル値を代入した場合と、インスタンス化を行った場合では、値の保持の方法が異なります。
 
この違いが表れるケースの一つとして、値が同一であるかどうかを確認するケースがあります。
リテラル値を代入した参照型変数同士を比較する場合は、インスタンス変数同士の比較と同じように、== で値が同一であることを確認できます。
しかし、インスタンス化を行った参照型変数の比較では、== で値が同一であることを確認することはできず、equalsメソッドを使用する必要があります。
 
これをソースコードで示すと以下の通りです。
(実行結果となる標準出力はコメントで示しています)
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        
        // リテラルを代入する場合
        String s1 = "hoge";
        String s2 = "hoge";
        System.out.println(s1 == s2); // true
        
        // インスタンス化する場合
        String s3 = new String("hoge");
        String s4 = new String("hoge");
        System.out.println(s3 == s4); // false
        System.out.println(s3.equals(s4)); // true
    }
}
 
----
 
参照型変数の比較について正しく説明する機会があったため、この記事を作成しました。