都啥时候了 , 不会有人还搞不懂new String(“abc“)创建了几个对象吧?快看看吧!

1 篇文章 0 订阅

最近也是狠狠的整理了一千多套简历模板 ,以及面试题、学习资源等 , 获取方式 : 微信公众号 :猿人刘先生(可以看看 写在最后的内容)

前言

new String(“abc”);创建了几个对象?等这一类问题是我们经常讨论的问题, 同时也是面试常问到的问题, 我们都知道在Java中从”.java"文件编译成".class"文件的过程,会有一个优化器去优化我们的代码, 为了彻底搞清楚这一类问题, 我们还得从底层原理开始分析.

JVM简介

JVM,全称Java Virtual Machine(Java虚拟机),是一个规范,用于在实际的计算机上仿真模拟各种计算机功能。它是一个虚构出来的计算机。JVM是执行代码并为该代码提供运行时环境的软件程序的规范。

具体来说,Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收器等组件。这些组件共同构成了JVM的运行环境。此外,JVM还包括内存区域,如程序计数器(线程私有)、虚拟机栈(线程私有)、本地方法区(线程私有)、堆(Heap-线程共享)和方法区/永久代(线程共享)等。

这篇文章重点涉及到的还是堆(Heap-线程共享)和栈(虚拟机栈(线程私有),

栈简介

栈内存则是用于存储方法调用、局部变量等运行时信息。栈内存遵循LIFO(后进先出)原则,一旦线程创建,就会在栈上为它的参数和局部变量分配空间。当线程执行完毕返回时,那些在栈上分配的内存空间会自动被释放,这一特性使得栈的管理变得相对容易。栈内存的大小在编译时期就已经被确定下来,所以它是一个相对较小的固定内存区域。并且,由于栈内存的分配和释放由编译器逐行执行,速度相对较快。

堆简介

堆内存是Java中用于存储对象实例和数组的内存空间。当我们使用new关键字创建一个对象或者创建一个数组的时候,就会在堆内存中分配一段连续的内存空间来存储这个对象或者数组。该空间可以根据需要进行动态分配和释放,而且在生命周期上与应用程序相同,只有在应用程序退出或对象被明确销毁后,才会被回收。但需要注意的是,由于堆内存需要在运行时动态分配,因此其存取速度较慢。

字符串常量池

在Java中,字符串常量池是一块特殊的内存区域,用于存储字符串对象。这是由于Java中的字符串是不可变的,一旦创建后不能被修改。为了提高性能和节省内存空间,Java引入了字符串常量池的概念来管理这些字符串对象。

当需要创建一个字符串对象时,Java会首先检查字符串常量池中是否已经存在相同内容的字符串对象。如果存在,则直接返回该对象的引用,而不会重新创建新的对象。这种机制可以避免重复创建相同内容的字符串,从而减少内存占用。

此外,由于字符串常量池中的字符串对象是唯一的,所以可以通过比较对象的引用地址来判断两个字符串是否相等,而不必逐个字符进行比较。这进一步提高了字符串比较的效率。
值得注意的是,Java中有几种不同的常量池,包括类常量池、字段常量池、方法常量池等。其中,字符串常量池是存储在Java堆内存中的。这是JVM为了提高性能和减少内存开销,在实例化字符串常量时进行的一些优化设计。

情况一

String a = “abc”;

分析

如果此时堆内存中的字符串常量池是没有abc这个字符串对象的, 此时会先在字符串常量池中创建"abc"这个字符串对象, 然后将这个字符串对象的引用(地址)压入栈中; 如果此时堆内存中的字符串常量池有abc这个字符串对象, 那就是直接将这个字符串对象的引用(地址)压入栈中;
所以 : 当字符串常量池有abc这个字符串对象时 , 创建了0个对象 , 没有abc这个字符串对象时创建了一个1对象

内存情况

情况二

String a = “a” + “b”;

分析

这里可能很多人都会有一个误解 , 就是创建了3个对象, "a"一个, "b"一个, “a” + “b"又是一个, 其实这种想法是错误的, 因为编译器在编译的时候就会把"a” + "b"进行拼接, 然后再去判断常量池中是否有"ab"这个字符串对象, 如果有就把这个对象的引用(地址压入栈中), 如果没有的话, 就会创建"ab"这个字符串对象, 然后把这个对象的地址压入栈中.
所以 : 当字符串常量池有ab这个字符串对象时 , 创建了0个对象 , 没有ab这个字符串对象时创建了一个1对象

内存情况

情况三

String a = “abc”;
String b= “a” + “b” + “c”;
System.out.println(a == b);

分析

在以上的两种情况中, 有个共同的点 , 就是如果存在的话就是创建了0个对象, 否则的话就是1个对象 , 所以这种情况就比较好理解了, 因为字符串常量池中没有"abc"这个对象, 所以会先进行创建, 然后"a" + “b” + “c"经过编译器拼接之后为"abc”, 在字符串常量池中有了, 所以不必创建, 那么在这个过程中, 创建了1个对象 , 并且a == b为true, 因为地址是相同的 , 都是"abc"这个对象的地址

内存情况

情况四

String a = “h”;

String b = “he”;
String c = a + “e”;

分析

前两行代码也是相同的逻辑, 如果h存在字符串常量池 ,则不创建,如果不存在, 则创建 , 如果he存在字符串常量池 ,则不创建,如果不存在, 则创建, 第三行代码需要注意的是 :a + “e” , "e"存在字符串常量池 ,则不创建,如果不存在, 则创建, a + “e” 这个运算的过程会在编译时期被编译器编译为使用StringBuilder拼接,所以一共至是多4个对象 , 分别为常量池中 : “h” ,“he” , “e” , 以及堆内存中StringBuilder

内存情况

情况五

String a = “h”;
String b = “he”;
String c = a + “e”;
String d = “h” + “e”;
System.out.println(c == d);
System.out.println(b == d);

分析

经过上面几种情况的分析,这个过程也是最多创建4个对象 , 变量c引用的是堆内存中new StringBuilder()的地址, 变量d引用的是字符串常量池中"he"的地址 ,因为"he"在编译时通过第二行代码就被写入了常量池,
所以说c == d为false , b == d为true

内存情况

情况六

String a = new String(“a”);

分析

这个就比较简单了,如果"a"存在字符串常量池则不创建,如果不存在则创建,然后new String在堆中申请了一块内存,所以至多是创建了2个

内存情况

情况七

String a = new String(“a”) + new String(“b”);

分析

这个先说答案吧,创建了6个对象,分别为字符串常量池"a" , “b” , new String()的两个,还有一个就是编译器会优化,也是同样的会创一个StringBuilder对象来拼接,那么这个StringBuilder就是第五个,拼接之后,调用toString()方法,又生成一个字符串“ab“, 所以说一共至多是6个对象,字符串常量池中三个 : “a” , “b” , “ab” , 堆中两个new String();一个一个new StringBuilder();

内存情况


如何在IDEA中使用字节码查看工具
首先需要在idea中进行如下配置

然后再想要查看的文件中右键


每次更改代码之后需要重新运行 , 然后进行步骤2

写在最后

如果您觉得这些文章对您有所启发和帮助,何不将它们与您的好友分享呢?这样,他们也能够享受其中的精彩内容,并从中获得启发。谢谢您的支持与分享!~
同时也希望您用发财的手帮忙点个关注,可以通过下方菜单点击福利领取上千套简历模板、几千道的面试题pdf以及几百G涵盖了Java开发,前端开发,小程序开发,数据库,测试等等的相关学习书籍与资料。
微信公众号 :猿人刘先生

另外也可以通过点击交流群按钮添加我好友,然后拉您到自己的创建的Java知识分享群。一起去讨论、学习、成长、进步,谢谢~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lingering fear

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值