提要
最近复习看到一道题目很有意思啊:
String s = new String("123");
问这样一行代码创建了几个String对象?
乍一看我其实以为和JDK的版本有关系,因为在深入理解java虚拟机第三版读书笔记02中1.6.2 intern方法有意思的点
曾经提到,JDK7之前,字符串常量池在方法区(永久代)中,而JDK7开始,就将字符串常量池移到了堆中。那么这个题目和这个知识点有关系吗?
然而仔细一看这行代码,便发现了蹊跷:我们平时定义字符串会使用这样的语法赋值吗?平时常用的给字符串赋值的方法应该是下面这样才对:
String s = "123";
那么这样两行代码有什么区别呢?先从搞清楚"123"
是什么开始:
字符串字面量
字符串的字面量和基本类型的字面量有点不同,基本类型可以写1
,true
,2l
这样的字面量。然而在Java中字符串不属于基本类型,String本身是一个类,那么 "123"
这样一个字面量肯定也是一个String的实例对象。
但是我们之前又提到了“字符串常量池”这个东西,到底什么时候字符串会从常量池中取值呢?下面通过几个例子验证一下:
public static void main(String[] args) {
String a = "111";
System.out.println("111" == a);
}
首先,"111"
是一个String的实例对象,将它赋值给a
,我们通常说,在Java中只有值传递,没有引用传递,就是指这里其实是把"111"
的引用地址通过值传递给了a
这个引用类型,使得它们指向了同一个对象。
而后面的输出语句中,再一次出现了"111"
这个字面量,通过它和a做==
比较,看它们指向的是不是同一个对象,结果输出true,说明重复出现的字面量其实指向的是同一个对象,这里可以猜测第一次出现的字符串字面量就创建了一个String实例,并放入了常量池中,后面出现的相同的字面量都是从常量池中取值。
那么既然第一次出现的字面量就会将创建的String实例放入字符串常量池,intern
这个方法有什么存在的必要呢?其实这是因为字符串不仅仅会以字面量的形式赋值。
比如下面这个方法:
public static void main(String[] args) {
String a = new StringBuilder().append("11").append("1").toString();
String b = "111";
System.out.println(a == b);
}
a是通过StringBuilder
来构造的,因此没有出现"111"
这个字符串字面量,相反而是出现了"11"
,"1"
这两个字面量,因此在与b进行比较的时候,输出的是false。
但假设我们仍想在第一行代码中就让"111"
进入字符串常量池,就可以使用intern
方法:
public static void main(String[] args) {
String a = new StringBuilder().append("11").append("1").toString();
a.intern();
String b = "111";
System.out.println(a == b);
}
这样就会输出true
通过这里的研究可以知道,下面这样的代码就是多此一举:
String a = "111";
a.intern();
因为第一行其实已经实现了intern
的效果。
如何解答这个问题
了解完了字符串字面量这个概念,相信回答这个问题也不难了,
String s = new String("123");
这行代码中,"123"
本身是一个String的实例对象,它的来源可能有两种情况:
1. 若之前字符串常量池中没有"123"
,这里就会创建一个字符串,并放入常量池
2. 若之前字符串常量池中有"123"
,这里就会直接指向常量池中的对象。
而s
是怎么来的?看一下这个构造方法(JDK8):
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
可以看到这个构造方法实际上是一个浅复制,只是会将字符串字面量中的value
字符数组复制过来。
所以这个问题应该这样回答:
如果在这段代码运行之前字符串常量池中已经有"123"
了,就只会创建一个String对象,相反则会创建两次,因为字符串字面量第一次出现会创建一个String实例对象。
与之对应的是,
String s = "123";
这样一行代码倒是有可能一个字符串对象也不创建,因为只是将常量池中对象的引用地址传递了一下。
原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/string-s-new-string123%e5%88%9b%e5%bb%ba%e4%ba%86%e5%87%a0%e4%b8%aastring%e5%af%b9%e8%b1%a1%ef%bc%9f/