记录一下java字符串常量池和intern方法相关的知识点。
首先解释一下什么是字符串的字面量,在java代码中用双引号括起来的一系列字符即为字符串字面量,如:“1”、“aa”、“abc123”等。
在java中所有字符串字面量都会在编译时在字符串常量池中创建对应的对象(字面量相加的情况比较特殊,会在后面提到)。如:
/* 在常量池中创建对象“123”,并将“123”的引用赋给s1 */
String s1 = "123";
/* 在常量池中创建对象“456”,并且在堆中创建新的对象并将堆中对象的引用赋给s2 */
/* 即:会分别创建2个对象 */
String s2 = new String("456");
接下来介绍一下intern方法的作用:
JDK 1.6以及之前:
当一个String对象调用intern方法时,如果池已经包含该对象对应的字符串,则返回池中字符串的引用。 否则,拷贝该对象到池中并返回池中对象的引用。
JDK 1.7以及之后:
当一个String对象调用intern方法时,如果池已经包含该对象对应的字符串,则返回池中字符串的引用。 否则,将该对象在堆中的引用放入池中并返回该引用。
验证:如下一段代码,可以知道s1是堆对象,s2是常量池中记录的对象,在JDK1.6中由于是拷贝了一个新的对象到常量池中,所以应该输出false;在JDK1.7中是将堆对象的引用存到常量池中,所以实际上是指向的同一地址,应该输出true。
public static void main(String[] args) {
String s1 = new String("abc") + new String("123");
String s2 = s1.intern();
System.out.println(s1 == s2);
}
执行结果:
可以看到实际执行结果与上面的分析一致。
- 各种情况下创建字符串对象:
1、直接使用字面量创建对象
/* 在池中创建对象"123" */
String s = "123";
2、字面量相加创建对象
/* 在编译时会自动转化成 String s = "abc123",所以只会在池中创建对象"abc123" */
String s = "abc" + "123";
注意:池中不会创建对象“abc”和“123”,该语句编译前后结果如下:
3、使用new String()创建对象
/* 会在池中创建对象"123"并且在堆中创建新的堆对象"123",s指向堆对象 */
String s = new String("123");
4、new String()相加创建对象
/* 会在池中创建对象"abc"和"123" */
/* 在堆中创建新的堆对象"abc"和"123" */
/* 两个堆对象相加后得到新的堆对象,s指向新的堆对象 */
String s = new String("abc") + new String("123");
5、字面量与new String()相加创建对象
/* 会在池中创建对象"abc"和"123" */
/* 在堆中创建新的堆对象"123" */
/* 相加后得到新的堆对象,s指向新的堆对象 */
String s = "abc" + new String("123");
- 调用String.intern()方法的情况分析(以jdk1.7及以上版本为例):
String s = "123"; /* true, 字面量直接放入池中,所以池中已存在"123",s.intern()返回池中对象 */ System.out.println(s == s.intern());
String s = new String("123"); /* false, 字面量直接放入池中,所以池中已存在"123", */ /* s.intern()返回池中对象,但是s指向堆对象 */ System.out.println(s == s.intern());
String s = new String("abc") + new String("123"); /* true, 字面量直接放入池中,所以池中已存在"abc"和"123",但是不存在"abc123" */ /* s.intern()会将堆对象"abc123"的地址放入池中并返回该地址,两者想等 */ System.out.println(s == s.intern());
注:在jdk1.6及以前的版本中会输出false。