JVM

new String()究竟创建几个对象?

2021-02-22  本文已影响0人  三也视界

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中,strafter_str不是同一个对象,这个应该没有疑问把,一个在堆上,一个常量池中,但是我们注意到str.valueafter_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.valuebefore_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真的是有很多”坑”,但是弄明白这些”坑”也是很有趣的。还有,这些都是我自己找资料,实践,总结,分析出来的,难免有错误存在,有发现的错误的同学,还请指正!

上一篇下一篇

猜你喜欢

热点阅读