深入汇编底层!看看JVM怎么区别对待各种创建字符串的方式【内含String = “xxx“和new String(“xxx“)等】

想必从Java学习之初,关于String创建了几个对象之类的问题就深深的折磨着各位Javaer,而且这个问题在各种面试场合中也是屡见不鲜,倘若不能深入的理解这个问题而只是空空死记硬背,面对别人的“灵魂提问”总有一天会措手不及。
于是,笔者就简单的通过对代码的反汇编,简单带大家了解一下各种创建字符串的方式,他们的运行原理都是什么样的吧
首先我们看以下代码片段:

String s1 = new String("abc");
String s2 = new String("abc");
String s3 = "abc";
String s4 = "abc";
String s5 = s1;
String s55 = s3;
String s6 = s1.intern();
String s7 = "a" + "b" + "c";

理论上,我依次通过五种方法创建了八个内容相同的字符串,稍有理论常识的我们都知道,即便内容相同,但他们未必都是相同对象,因为地址是不同的。那么他们互相之间,哪些是相同对象,哪些不同呢?
我们通过 == 运算符来比较一下:

        System.out.println(s1 == s2); // false
        System.out.println(s1 == s3); // false
        System.out.println(s1 == s5); // true
        System.out.println(s1 == s6); // false

        System.out.println(s3 == s4); // true
        System.out.println(s3 == s5); // false
        System.out.println(s3 == s55); // true
        System.out.println(s5 == s55); // false
        System.out.println(s3 == s6); // true

        System.out.println(s1 == s7); // false
        System.out.println(s3 == s7); // true
        System.out.println(s6 == s7); // true

运行结果如备注所示,与你想象中是否相同呢?
可以看到的是,二者为同一个对象的,只有以下几种情况:

1、s1和s5、s3和s55:直接引用已存在的字符串
2、s3和s4:通过String = "xxx"的方式生成相同字符串
3、s3和s6:intern相关,稍后讲讲
4、s3和s7、s6和s7:通过String = “x” + “x” + “x” 拼接而成的字符串与已存在的相同字符串

为什么呢?下面我们通过javap对上述代码进行反汇编,看看这些字符串都是如何生成的:

在这里插入图片描述
全览如上图所示,有关JVM的汇编指令含义在此就不多分析,可以通过其他博文简要了解,下面我们直接来分析一下每一个字符串都是如何生成的:
一、s1s2
在这里插入图片描述
在这里插入图片描述
可以看到,通过new String("xxx")这一方式生成的字符串,依次经过了创建->将栈顶数值复制并压入栈顶->将常量值从池中推至栈顶->调用构造方法->将栈顶引用数值存入本地变量这一过程。可以看到的是,二者分别创建了两个不同的对象,故在地址比较时,结果自然也是不同的。
二、s3s4
在这里插入图片描述
在这里插入图片描述
可以看到,通过String s = "xxx"这一方式生成的字符串,本质上是通过常量池中取值(若存在)->存入本地变量 这一过程。当我们创建s3的时候,也同时在常量池中放入了abc,这样在创建s4时,我们便直接从常量池中到该值并存入,二者本质上为引用了同一个值,故也是相等的。
这时候你可能会问:难道s1中new String没有在常量池中放值吗?这个问题后面会再次解释。
三、s5s55
在这里插入图片描述
这两个有点儿意思:他们分别将new String(“xxx”) 和String = "xxx"生成的两个已经证明是不相同的对象赋给本地值,我们看到他们创建的方式均为将引用类型的本地变量推至栈顶->将栈顶引用存入本地变量,而我们已经知道s1与s3二者并不是相同对象,故他们的引用变量自然也是不同的。
四、s6
在这里插入图片描述
可以看到,通过intern()方式创建的字符串,经过了将引用类型的本地变量推至栈顶->调用实例方法->将栈顶引用存入本地变量这一过程,是将一个已存的对象的值推进常量池的行为。然而原变量和新生成的变量并非相同对象
不过我们可以看到,由于s6与s3均位于常量池中,其二者在JVM中会被优化为相同对象。通过这个小实验我们也可以认识到,将一个new出来的对象的值推入常量池这个行为,新生成的本地变量与原先的本地变量不是相同对象。
五、s7
在这里插入图片描述
可以看到,通过String = “x” + “x” + "x"这一方式生成,其实在编译期会被优化成一个对象,然后存入常量池并赋值给本地变量,而本例中由于常量池中已经存在abc(即s3),所以会直接引用常量池中的abc,所以s7与s3,s6是同一个对象。

总结:

我们可以看到,一个简单的String对象的生成,不同的方式下有着或不同或相同的执行过程,这也会导致其生成结果的各不相同。其实这个问题要想深挖下去还有更多值得探讨的问题,可惜笔者笔力有限,暂时只想到这几种情况。后续如果有新的见解,也会及时在这里继续补充。文章中若有错误内容,恳请各位高手于评论区不吝赐教,祝大家共同进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值