String s = new String(“ abc “)面试题之到底创建了几个对象?

这是一道非常经典的面试题

首先这个题的关键,就在于有一个字符串常量池的概念

我们省去八股文介绍

直接从创建string对象的两种方式开始讲起

我们先抛出一个问题: s1和s2分别创建了几个对象?S1==S2?(小白必死题)

诶~都是创建一个"abc"字符串对象,那么有什么区别呢?

不急~来看一下这段代码

先从String s2=new string("abc")开始

好!我们要创建对象了!

老子现在告诉字符串常量池:

“诶嘿嘿~常量池小老弟~大爷准备在你那创建一个"abc"字符串对象,你那边有没有一样的,有的话就不用在你那再新建一个了,没有的话麻溜的给建一个”

翻译过来就是:

创建"abc"对象在这个过程中,java虚拟机会检查常量池里有没有这个对象,如果有,就不在常量池里面创建任何对象,如果没有,那就在常量池里创建一个"abc"字符串对象。

如图:

因为String s2=new string("abc")是第一条执行语句,很明显我们之前没有创建过"abc"这个对象

常量池赶紧说:”大锅大锅!我没有!我没有!你快来建呀! ~”

于是我们心满意足的在常量池里新建了一个"abc"对象

 好,我们在常量池里面建了一个"abc"之后,正准备开溜

 常量池又说话了,“大锅....你创建这个字符串变量有没有打双引号啊,是不是通过New创建的啊?”

老子于是虎躯一震,两眼一瞪!这里面难道还有什么玄机吗???

常量池又被吓坏了,赶紧说”有的!有的!大锅”

(重点来了!!!敲黑板!)

还是这个栗子,创建一个"abc"字符串

           String s1="abc"

           String s2=new string("abc")

 无论是用哪种方式创建JVM首先会去字符串常量池池中查找是否存在如"abc"这个对象,类似看常量池里有没有备份过

1.直接以双引号方式创建的字符串对象

if(常量池里没有备份过"abc"){

则在常量池中创建"abc"这个对象,然后将池中"abc"这个对象的引用地址返回给字符串常量s1, 这样s1会指向常量池中"abc"这个字符串对象}

else if(常量池里备份过"abc"){

 则不创建任何对象,直接将常量池中"abc"这个对象的地址返回,赋给字符串常量。}

 注意:双引号创建的只会在字符串常量池中存储,而且相同的内容只会在其中存储一份

2.采用new关键字新建一个字符串对象

老样子,JVM首先会去字符串常量池池中查找是否存在如"abc"这个对象,类似看常量池里有没有备份过

if(常量池存在"abc"){

常量池不创建, 直接在堆内存中创建一个"abc"字符串对象,返回堆中对象的地址}

else if(常量池不存在"abc"){

常量池中创建一个"abc"字符串对象,然后再在堆中创建一个"abc"字符串对象,然后将堆中这个"abc"字符串对象的地址返回,赋给s2引用,这样,s2指向了堆中创建的这个"abc"字符串对象。}

注意:通过New关键字创建对象,每New一次都会在堆内存里产生一个新的对象,这样可以保证多个引用可以同时指向字符串池中的同一个对象

3.那么既有双引号,又是通过New创建的对象,常量池里又没有备份,就会在常量池和堆内存里面同时创建一个对象!

啊对!没错!我们再看一下这个代码!这就是我们这个题的关键

String s2=new string("abc")

“abc"   双引号

over~   

new创建

over~   

好家伙,就是这小子一下子处了两个对象!!!

 大家看到了吧,证据确凿!!这小子不仅在常量池里摆了个摊,还在堆内存里面开了个店,脚踏两只船,渣男.....(我日*&*%&¥)

这个时候我们再看一下最初的那道小白杀手题

 很明显s2又有双引号又有new,这小子创建了两个对象,常量池和堆内存各创建了一个

我们之前说过,通过New关键字创建的对象,返回的是堆内存中的地址

也就是说此时S2正指向的是堆内存中的“abc"

那么S1呢?

S1说老子TM一个都没创建!劳资创建了0个对象!!

。。。。为什么会这样呢? 

String s1="abc"

S1老哥是通过双引号创建的,只能常量池创建对象,但是“abc"已经存在了

根据公式则不创建任何对象,直接将常量池中"abc"这个对象的地址返回,但是其实他一个对象都没有创建

S1指向常量池"abc",S2指向堆内存"abc"

那么System.out.print(s1==s2) 的答案大家应该知道了吧,等于false

好,这个面试题算是做完了~

拓展题目

s1跟s3指向的地址相同吗?

首先S1,S2这两个都是直接用双引号创建的,没有用new创建,那么都只在常量池里创建了一个对象

S3呢,我们说过,要直接以双引号创建的对象才会在常量池里存储,直接!直接!直接!重要的事情说三遍,你看他直接吗?

所以说,S3并不算直接用双引号创建,是指向堆内存中的"abc",而S1指向常量池中的"abc",所以两者不相等!

你以为你又懂了。。。那我再来一个

s1==s2吗?

分析分析,s1指向常量池毋庸置疑,那么S2怎么说,我看他不是很直接的样子,是不是指向堆内存呢?

好的,你又错了,这波啊,这波是因为JAVA存在编译优化机制,”a"+"b"+"c"会直接转成“abc",

所以S2的形成代码在执行时同样是用双引号直接创建的,同样是"abc",所以S1和S2都是指向常量池里面的"abc",所以S1==S2.....

此时此刻我想起了一句诗,假如生活欺骗了你,不要悲伤,不要害怕。。。

其实我们一般创建的字符串对象都会保存在常量池里面,这也是为什么说字符串对象是不可改变的,是final的,它的值一经创建就不可改变,你变的都是在堆内存里的。

举个栗子:

String str="hello"

str +="world"

输出str后发现输出"hello world"

你以为你变了str的值吗?NO!!

他会在常量池里面存储两个不同的对象,一个是"hello",一个是"world"

你看到的"hello world"实际上是堆内存里面的对象

为什么要搞这么复杂呢?

因为JVM为了提升性能和减少内存开销,避免字符串的重复创建,省去了创建相同字符串的时间,其维护了一块特殊的内存空间。

而且因为常量池里面的String对象是可以被共享使用的,所以一旦改变就可能导致程序的混乱。

字符串常量池由string类私有的维护,我们可以调用Intern()方法来访问常量池

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值