new String()究竟创建几个对象?
new String()究竟创建几个对象?
1. 由来
遇到一个Java面试题,是关于String
的,自己对String
还有点研究?下面是题目的描述:
在Java中,
new String("hello")
这样的创建方式,到底创建了几个String
对象?
题目下答案,各说纷纭,有说1个的,有说2个的。我觉得都对,但也都不对,因为要加上一定的条件,下面来分析下!
2. 解答
2.1. 分析
题目中的String
创建方式,是调用String
的有参构造函数,而这个有参构造函数的源码则是这样的public String(String original)
,这就是说,我们可以把代码转换为下面这种:
String temp = "hello"; // 在常量池中
String str = new String(temp); // 在堆上
这段代码就创建了2个String
对象,temp
指向在常量池中的,str
指向堆上的,而str
内部的char value[]
则指向常量池中的char value[]
,所以这里的答案是2个对象。(这里不再详述内部过程,之前的文章有写,参考深入浅出Java String)
那之前我为什么说答案是1个的也对呢,假如就只有这一句String str = new String("hello")
代码,并且此时的常量池的没有"hello"
这个String,那么答案是两个;如果此时常量池中,已经存在了"hello"
,那么此时就只创建堆上str
,而不会创建常量池中temp
,(注意这里都是引用),所以此时答案就是1个。
当然,光说不练假把式,下面就来用例子验证一下:
2.2. 验证
不啰嗦,直接看第一段代码:
// 代码1
public static void main(String[] args) {
String str = new String("hello");
String after_str = "hello";
System.out.println(str + after_str);
}
我们在println
语句那里加个断点,debug
下,如下图:
[图片上传失败...(image-a19eb5-1613797688333)]
可以看到在代码1中,str
和after_str
不是同一个对象,这个应该没有疑问把,一个在堆上,一个常量池中,但是我们注意到str.value
和after_str.value
的地址确是同一个,即是同一个char
数组,所以可以理解为,当执行str
那句代码时,创建了两个String
对象,一个在常量池,一个在堆上,接下来到执行after_str
这句时,这种方式创建的字符串都在常量池中,但是如果常量中如果有,就直接返回了,所以这里返回的是上一句在常量池那里创建的String
,所以str
这句代码创建的是2个对象。
接下来再看一段代码:
// 代码2
public static void main(String[] args) {
String before_str = "hello";
String str = new String("hello");
System.out.println(str + before_str);
}
同样在println
打断点,如下图:
[图片上传失败...(image-52383e-1613797688333)]
在代码2中,str.value
和before_str.value
也是用的同一个char
数组,但这里的代码可不是和代码1相同的,这里我们把"hello"
提到前面创建,这也就是说明当执行str
那句代码时,没有再在常量池中创建"hello"
了,所以str
这句则是创建了1个对象。
2.3. 补充
这里突然想到,还要补充一下上面如果常量池中没有,则创建2个对象
,很多人会误解这句话的含义,这个常量池,不是你没手动的创建,常量池中就没有东西,这个理解是错误的,大家一定要修正它。在你的程序启动时,就已经装载来很多class
,这里面也有字符常量的创建,只是不是你自己创建的,你只是不知道而已。
例如下面这段代码:
// 代码3,JDK >= 1.7
public static void main(String[] args) {
String str1 = new StringBuilder("hel").append("lo").toString();
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str1.intern() == str1); // true
System.out.println(str2.intern() == str2); // false
}
(关于intern
在不同JDK的不同表现,之前也说过了,详情请看深入浅出Java String)
代码3这里,str1
的结果是很多人都能理解的,但是str2
的结果为false
就说明,常量池中已经有了"java"
了,这里很多人不解,这个就是系统装入的字符常量,除了"java"
,类似的还有"false"
,"true"
(在java.lang.String
里)等等。
注:System类里面有一行代码 sun.misc.Version.init();Version类里面有一个静态常量字符串launcher_name=“java”
3. 总结
通过这个面试题,可以看到String
真的是有很多”坑”,但是弄明白这些”坑”也是很有趣的。还有,这些都是我自己找资料,实践,总结,分析出来的,难免有错误存在,有发现的错误的同学,还请指正!