字面量
在Java中,字面量是指直接使用在代码中的常量值。例如,"Vincent"
和"hello"
就是字符串字面量,123
和3.14
则是整型和浮点型字面量。字面量可以直接在代码中使用,不需要使用new
关键字来创建对象。
在使用字面量创建字符串时,Java会首先在编译时将这些字符串放入一个特殊的存储区域,这个区域被称为编译时常量池。编译时常量池是每个类的常量池的一部分,用于存储类的静态常量和字面量。
例如,在以下代码中:
String str1 = "Vincent";
String str2 = "hello";
字面量"Vincent"和"hello"在编译时会被放入编译时常量池。在运行时,如果有多个字符串使用相同的字面量,它们会共享同一个在编译时常量池中的对象。
运行时常量池
运行时常量池是在运行时创建的,用于存储在类加载后动态生成的常量。它是在方法区中的一部分,用于存储类的常量、静态变量、字符串字面量等信息。
在运行时常量池中,除了存储字面量外,还可以动态生成其他常量。例如,通过调用intern()
方法可以将一个字符串添加到运行时常量池中。
让我们通过代码实现来加深理解:
public class RuntimeConstantPoolDemo {
public static void main(String[] args) {
String str1 = "Vincent"; // 在编译时常量池中创建 "Vincent" 字符串对象
String str2 = "hello"; // 在编译时常量池中创建 "hello" 字符串对象
String str3 = "Vincent"; // 字符串常量"Vincent"已存在于编译时常量池中,直接返回引用
String str4 = new String("Vincent"); // 在堆中创建一个新的字符串对象
String str5 = str4.intern(); // 将str4字符串对象添加到运行时常量池中,并返回引用
// 判断是否为同一个引用
System.out.println("str1 == str3: " + (str1 == str3)); // 输出 true,因为它们都是编译时常量池中的同一个对象
System.out.println("str1 == str4: " + (str1 == str4)); // 输出 false,因为str4在堆中创建了新的对象
System.out.println("str1 == str5: " + (str1 == str5)); // 输出 true,因为str5是从运行时常量池中返回的引用
// 判断内容是否相同
System.out.println("str1.equals(str4): " + str1.equals(str4)); // 输出 true,因为内容相同
}
}
输出结果为:
str1 == str3: true
str1 == str4: false
str1 == str5: true
str1.equals(str4): true
从输出结果可以看出:
str1
和str3
是同一个引用,因为它们都指向编译时常量池中的同一个对象。str1
和str4
不是同一个引用,因为new String("Vincent")
在堆中创建了新的对象。str1
和str5
是同一个引用,因为str5
是从运行时常量池中返回的引用。
总结:
- 字面量是直接使用在代码中的常量值,例如字符串字面量:"Vincent"和"hello"。
- 编译时常量池是在编译时存储字面量的特殊存储区域,用于存储类的静态常量和字面量,它属于类的一部分。
- 运行时常量池是在运行时动态生成的,用于存储类的常量、静态变量、字符串字面量等信息,它属于方法区的一部分。
- 运行时常量池中的字符串对象可以通过调用
intern()
方法将对象添加到其中,从而实现共享字符串对象的效果。
intern()
intern()
是一个方法,定义在java.lang.String
类中。它的作用是将字符串对象添加到运行时常量池中,并返回运行时常量池中该字符串的引用。如果运行时常量池中已经存在相同内容的字符串,则直接返回该字符串的引用。
intern()方法的工作原理
- 当调用
intern()
方法时,JVM首先检查该字符串是否已经在运行时常量池中。如果在运行时常量池中找到了相同内容的字符串,则直接返回该字符串在常量池中的引用。 - 如果运行时常量池中没有找到相同内容的字符串,JVM会将该字符串添加到运行时常量池中,并返回新添加的字符串在常量池中的引用。
代码实现
让我们通过代码来演示intern()
方法的使用和效果:
public class InternMethodDemo {
public static void main(String[] args) {
String str1 = "hello"; // 字符串"h"添加到运行时常量池
String str2 = new String("hello"); // 在堆中创建新的字符串对象
String str3 = str2.intern(); // 将str2所引用的字符串对象添加到运行时常量池,并返回常量池中的引用
// 判断是否为同一个引用
System.out.println("str1 == str2: " + (str1 == str2)); // 输出 false,str1指向常量池,str2指向堆
System.out.println("str1 == str3: " + (str1 == str3)); // 输出 true,str3是从运行时常量池中返回的引用
// 判断内容是否相同
System.out.println("str1.equals(str2): " + str1.equals(str2)); // 输出 true,内容相同
System.out.println("str1.equals(str3): " + str1.equals(str3)); // 输出 true,内容相同
}
}
输出结果为:
str1 == str2: false
str1 == str3: true
str1.equals(str2): true
str1.equals(str3): true
从输出结果可以看出:
str1
和str2
不是同一个引用,因为str2
使用new
关键字在堆中创建了新的对象。str1
和str3
是同一个引用,因为str3
是从运行时常量池中返回的引用。str1
和str2
的内容相同,因为它们都是"hello"这个字符串。str1
和str3
的内容相同,因为它们都是"hello"这个字符串。
总结:
intern()
方法将字符串对象添加到运行时常量池,并返回常量池中该字符串的引用。- 如果运行时常量池中已经存在相同内容的字符串,则直接返回该字符串的引用。
intern()
方法可以用于节省内存,因为共享相同内容的字符串可以在运行时常量池中重用。但过度使用intern()
方法也可能导致运行时常量池过大,从而影响性能。因此,应谨慎使用该方法,只在有必要时使用。