前言
没啥子前言,就是之前写的一篇String笔记被别人夸了一下,然后发现自己写的有些地方是错了,所以好好整了一篇,还想体验一下被夸的滋味。
环境
jdk1.8.0_171
win10家庭版
Talk is cheap , show me you code
//eg1
Strings1=newString("aa");
先来看eg1,这一个最简单的创建String对象,老生常谈,这个过程创建了几个对象?这个问题主要点在于在常量池中有没有创建aa常量,不多叭叭,看图。
Constant pool就是常量池,可以看到常量池中存在aa,也就是说eg1这个过程在堆中创建了一个对象实体,在常量池中也存了一份aa。s1指向的是堆中的对象实体。那么再来看一个。
//eg2
Strings1=newString("aa")+newString("bb");
嘿嘿,有意思了,常量池中只有aa和bb,new String("aa") + new String("bb")底层实际上是创建了一个StringBuilder,然后调用StringBuilder调用append拼接aa和bb,但是常量池并没有拼接之后的aabb,不是创建对象之前会先去常量池查找有没有对应的常量吗,如果没有就创建一个吗?为什么没有在常量池中创建aabb呢?突然想起来我师傅当时面试我的时候好像问过我,赶紧跑去问了一下,师傅给了我两拳,然后不紧不慢的打开浏览器输入了关键词。。。。。
经过一番激烈的百度,终于找到原因,原来是因为new String("aa") + new String("bb")是在运行期间才能确定类型的,所以不会放到常量池中。看图
再来
//eg3
Strings1=newString("aa")+newString("bb");
Strings2="aabb";
System.out.println(s1==s2);//false
不叭叭,直接看图
虽然看反编译结果常量池中有aabb,但是那个aabb是s2 = "aabb"放进去的,s1还是指向堆中的对象实体。所以s1 != s2;
//eg4
Strings2="aabb";
Strings1=newString("aa")+newString("bb");
System.out.println(s1==s2);//false
这不是还是输出false吗?和pd3有什么不同,鹏鹏你是傻了吗?我知道在座的各位都理解了,但是我还是得把所有情况都实验一遍不是?谁让我是暖男呢?
s1指向堆中的对象其实是没有创建char数组去存aabb的,直接存了一个地址值,这个地址值就是常量池中aabb的地址值。不用多说,各位人才肯定都懂的。那么继续喽!
//eg5
Strings1=newString("aa")+newString("bb");
Strings2=s1.intern();
System.out.println(s1==s2);//true
先来说一小哈哈这个intern方法,来看看周志明前辈的深入理解Java虚拟机中怎么说的:
“运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。”
想看这一部分的可以去深入理解Java虚拟机第三版的2.2.6章节查看。这本书真是太好了,简直是面试必看,哈哈哈。
我的理解:String的intern就是在运行期间检查常量池中是否有该字符串,如果有就直接返回常量池的中该常量的引用。如果常量池中没有,就得分版本了,在jdk1.7之前是直接将堆中的对象复制一份放入常量池;但是1.7之后就是将堆中对象的引用复制一份放入常量池中。看图
可以看到常量池中只有aa 和 bb,并没有aabb,所以常量池中只是存了aabb在堆中的地址值。
s2的存的地址值和常量池中那个是一样的,都是指向堆中的aabb对象实体。所以s1 == s2;
再来再来
//eg6
Strings1="aabb";
Strings2=newString("aa")+newString("bb");
Strings3=s1.intern();
System.out.println(s1==s2);//false
System.out.println(s2==s3);//false
System.out.println(s1==s3);//true
这儿常量池中aabb是s1 = "aabb"存进去的,s2=new String("aa") + new String("bb")只在常量池中存了"aa"和"bb"常量,在堆中存了"aabb"对象实体,s2指向的还是堆中"aabb"的对象实体,s3 = s1.intern()的时候,直接将常量池中aabb的地址值返回。所以s1 和 s3指向的都是常量池中的aabb,s2指向的是堆中的aabb对象实体。什么?还不理解,还不理解那就太好了,要不然我图不是白画了吗?
这下该懂了吧,什么?还不懂,那看下一个例子。
//eg7
Strings1=newString("aa")+newString("bb");
Strings3=s1.intern();
Strings2="aabb";
System.out.println(s1==s2);//true
System.out.println(s2==s3);//true
System.out.println(s1==s3);//true
s1 = new String("aa") + new String("bb")这一步操作在堆中创建了aabb对象实体,当常量池中并没有aabb,s3 = s1.intern()这一步操作是先去常量池中查找有没有aabb常量,发现没有,就将堆中aabb对象实体的地址复制一份放入常量池中(这是基于jdk1.7之后,我觉得jdk1.8之前的有时间可以去琢磨琢磨,没时间就别琢磨了,都出到jdk14了还琢磨那么古老的东西干嘛对吧,反正我用的一直都是1.8;之前琢磨jdk1.6也是被逼无奈,面一个初级java开发,人家都问我低耦合高并发各种锁的底层什么jdk1.8之前和之后有什么区别,我的老哥哥,我初来咋到你就想让我知道那么多,我还有时间写bug找你们帮我改吗?)
上面这几个例子都是基础,其他的面试题也是根据这几种情况改吧改吧出来的,要是上面这几个懂了,其他的也就是多思考一下就OK了。但是你懂了吗?
//eg8
Strings1=newString("aa");//在堆中创建aa对象,常量池放入aa
Strings2="aa";//常量池中已经有aa了,所以直接把堆中地址拿过来就行
Strings3="bb";//常量池中放入bb
Strings4=s1+s2;//涉及到对象的相加,所以肯定是在堆中创建对象
Strings5=s2+s3;//涉及到引用相加,不能确定返回结果类型,所有是在堆中创建对象
Strings6="aa"+"bb";//常量相加,在常量池中直接放入
Strings7=newString("aabb");//堆中创建aabb对象,但是这个对象的并没有存aabb,而是存了一个指向常量池中aabb的地址值
Strings8="aabb";//常量池中已经有aabb了,所以直接将常量池地址值拿过来就好
System.out.println(s1==s2);//
System.out.println(s4==s5);//
System.out.println(s5==s6);//
System.out.println(s4==s7);//
System.out.println(s5==s7);//
System.out.println(s6==s7);//
System.out.println(s4==s8);//
System.out.println(s5==s8);//
System.out.println(s6==s8);//
System.out.println(s7==s8);//
各位人才可以把结果发到评论区哟!大家互相讨论,说不准就擦出了爱情的火花呢?
什么,你说连个妹子都没有怎么可能擦除爱情的火花?要我说,都单身那么多年了,你是不是该降低一下标准了?
我可真会yy,哈哈哈,想着自己写的文章能被很多人看见,你们是体会不到我现在的内心戏有多足,哈哈哈。
想来想去,我还是把答案写出来吧,别过两天我也不知道正确答案了,还得去运行一下。
System.out.println(s1==s2);//false
System.out.println(s4==s5);//false
System.out.println(s5==s6);//false
System.out.println(s4==s7);//false
System.out.println(s5==s7);//false
System.out.println(s6==s7);//false
System.out.println(s4==s8);//false
System.out.println(s5==s8);//false
System.out.println(s6==s8);//true
System.out.println(s7==s8);//false
上面每一步我也都注释了,不管对不对反正今天的我比昨天的我更强大了!!哈哈哈!!
各位人才,各位大佬,有不对的地方各位请不吝指教!都看到这儿了,点赞收藏转发三连一下?祝各位早日找到女朋友。喜欢的朋友可以关注一下我的公众号敲代码的蛋蛋,一起成长,一起骚起来呀!!!
我是Java鹏鹏,今天的你是否比昨天的你更优秀了呢?
参考资料 <深入理解Java虚拟机第三版>