リテラルはプログラミング言語で変数に直接割り当てられる固定値を意味します。この記事ではJavaのリテラルがメモリにどのように格納され管理されるかを見ていきます。
リテラルはコードで値として直接現れるデータを指します。例えば、数字10
、文字列"Hello"
、論理値true
などはすべてリテラルです。簡単に言えば、リテラルはコードで使用されるデータそのものです。
リテラルの代表的な種類は次のとおりです:
10
、-42
、0
、9999L
3.14f
、0.5
'A'
、'B'
"Hello"
、"World"
true
、false
リテラルがメモリに格納される方式は、変数が基本型かラッパークラスまたは文字列かによって異なります。
基本型変数にリテラルを代入すると、その値は変数のメモリ空間に直接格納されます。例えば、整数リテラル10
を基本型変数に割り当てると、変数のメモリ空間に10
という値が直接格納されます。これは処理速度が速く効率的です。
コード例:
public class Main {
public static void main() {
int intVal = 3;
}
}
メモリダイアグラム:
基本型変数はコールスタックにビットデータを直接格納します。
基本型の参照型であるラッパークラス(Integer、Doubleなど)と文字列クラスは、定数プール(Constant Pool)という特別な空間に格納されます。定数プールに同じ値がすでに存在する場合、既存のアドレスを返してメモリを節約します。
コード例:
public class Main {
public static void main(String[] args) {
Integer intVal = 3;
String str = "hello";
}
}
メモリダイアグラム:
ラッパークラスと文字列は定数プールに格納された値を参照します。
ラッパークラスと文字列リテラルは定数プールのアドレスを参照するため、同じ値を複数回生成しても常に同じインスタンスを参照します。一方、new
キーワードを使用してオブジェクトを生成すると、定数プールの代わりにヒープメモリに新しいオブジェクトが生成されます。
コード例:
public class ConstantPoolTest {
public static void main(String[] args) {
A a = new A();
B b = new B();
System.out.println(a.instanceStr == b.instanceStr); // true
System.out.println(a.instanceStr == b.newStr); // false
System.out.println(a.instanceStr == b.internStr); // true
System.out.println(a.instanceStr.equals(b.newStr)); // true
System.out.println(a.instanceInt == b.instanceInt); // true
System.out.println(a.instanceInt == b.newInt); // false
System.out.println(a.instanceInt == b.internInt); // true
System.out.println(a.instanceInt.equals(b.newInt)); // true
}
}
class A {
String instanceStr = "instanceStr";
Integer instanceInt = 1;
}
class B {
String instanceStr = "instanceStr";
String newStr = new String("instanceStr");
String internStr = newStr.intern();
Integer instanceInt = 1;
Integer newInt = new Integer(1);
Integer internInt = newInt.intValue();
}
intern()
メソッドを使用すると、文字列定数プールで同じ値を見つけてアドレスを返します。
リテラルで生成されたオブジェクトは同じ定数プールアドレスを共有するため比較結果が「真」になりますが、new
キーワードを使用して生成したオブジェクトはヒープメモリに別々に生成されるため比較結果が「偽」になります。