Java中,那些关于String和字符串常量池你需知道的东西(1)

[图片上传失败…(image-7210d9-1611541724305)]

很显然不是JDK的缺陷,JDK虽然确实有设计缺陷,但不至于这么明显,这么愚蠢。

那下面的那个String对象是干什么的呢?

答案是用于驻留到字符串常量池中去的,注意,这里我用了一个 驻留 ,并不是直接把对象放到字符串常量池里面去,有什么区别我们后面再讲。

这里出现了 字符串常量池 的概念,我在 String s = new String(“xyz”)创建了几个实例你真的能答对吗? 中也有过比较详细的介绍,有兴趣的可以去看一下,这里不再重复了。

你只需要知道,字符串常量池在JVM源码中对应的类是StringTable,底层实现是一个Hashtable。

Java中,那些关于String和字符串常量池你需知道的东西

我们以 String s = new String(“xyz”); 为例:

首先去找字符串常量池找,看能不能找到“xyz”字符串对应对象的引用,如果字符串常量池中找不到:

  • 创建一个String对象和char数组对象

  • 将创建的String对象封装成HashtableEntry,作为StringTable的value进行存储

  • new String(“xyz”)会在堆区又创建一个String对象,char数组直接指向创建好的char数组对象

如果字符串常量池中能找到:

  • new String(“xyz”)会在堆区创建一个对象,char数组直接指向已经存在的char数组对象

Java中,那些关于String和字符串常量池你需知道的东西

而 String s = “xyz”; 是怎么样的逻辑:

首先去找字符串常量池找,看能不能找到“xyz”字符串的引用,如果字符串常量池中能找不到:

  • 创建一个String对象和char数组对象

  • 将创建的String对象封装成HashtableEntry,作为StringTable的value进行存储

  • 返回创建的String对象

如果字符串常量池中能找到:

  • 直接返回找到引用对应的String对象

Java中,那些关于String和字符串常量池你需知道的东西

总结而言就是:

对于 String s = new String(“xyz”); 这种形式创建字符串对象,如果字符串常量池中能找到,创建一个String对象;如果如果字符串常量池中找不到,创建两个String对象。

对于 String s = “xyz”; 这种形式创建字符串对象,如果字符串常量池中能找到,不会创建String对象;如果如果字符串常量池中找不到,创建一个String对象。

image.png

所以,在日常开发中,能用 String s = “xyz”; 尽量不用 String s = new String(“xyz”); ,因为可以少创建一个对象,节省一部分空间。

需要强调的是,字符串常量池存的不是字符串也不是String对象,而是一个个HashtableEntry,HashtableEntry里面的value指向的才是String对象,为了不让表述变得复杂,我省略了HashtableEntry的存在,但不代表它就不存在。

上文提到的驻留就是新建HashtableEntry指向String对象,并把HashtableEntry存入字符串常量池的过程。

在网上一些文章中,一些作者可能是为了让读者更好的理解,省略了一些这些,一定要注意辨别区分。

Java中,那些关于String和字符串常量池你需知道的东西

达成以上共识之后,我们再回顾一下那个老套的笔试题。

String s1 = new String(“xyz”);

String s2 = “xyz”;

//为什么输出的是false呢?

System.out.println(s1 == s2);

//为什么输出的是true呢?

System.out.println(s1.equals(s2));

有了上面的基础之后,我们画出对应的内存图,s1 == s2为什么是false就一目了然了。

Java中,那些关于String和字符串常量池你需知道的东西

因为equals方法比较的真正的char数据,而s1和s2最终指向的都是同一个char数组对象,所以s1.equals(s2)等于true。

关于他们最终指向的都是同一个char数组对象这一观点,也可以通过反射证明:

Java中,那些关于String和字符串常量池你需知道的东西

我修改了str1指向的String对象的value,str2指向的对象也被影响了。

Java中,那些关于String和字符串常量池你需知道的东西

字符串拼接

=====================================================================

现在,我们再来看一下变式题:

String s1 = “aa”;

String s2 = “bb”;

String str1 = s1 + s2;

String str2 = “aabb”;

//为什么输出的是false

System.out.println(str1 == str2);

对于这个题目,我们需要先看一下这段代码的字节码。

image.png

字节码指令看不懂没有关系,看我用红色框框起来的部分就行了,可以看到居然出现了StringBuilder。

什么意思呢,就是说 String str1 = s1 + s2; 会被编译器会优化成 new StringBuilder().append(“aa”).append(“bb”).toString();

StringBuilder里面的append方法就是对char数组进行操作,那StringBuilder的toString方法做了什么呢?

Java中,那些关于String和字符串常量池你需知道的东西

从源码中可以看到,StringBuilder里面的toString方法调用的是String类里面的String(char value[], int offset, int count) 构造方法,这个方法做了什么呢?

  • 根据参数复制一份char数组对象。复制了一份!

  • 创建一个String对象,String对象的value指向复制的char数组对象。

注意,并没有驻留到字符串常量池里面去,这个很关键!!!画一个图理解一下:

Java中,那些关于String和字符串常量池你需知道的东西

也就是说str2指向的String对象并没有驻留到字符串常量池,而str1指向的对象驻留到字符串常量池里面去了,且他们并不是同一个对象。所以str1 == str2还是false

因为复制一份char数组对象,所以如果我们改变其中一个char数组的话,另一个也不会造成影响:

image.png

把其中String变成丑比之后,另一个还是帅比,也说明了两个String对象用的不是同一份char数组。

Java中,那些关于String和字符串常量池你需知道的东西

intern方法

========================================================================

上面说到,调用StringBuilder的toString方法创建的String对象是不会驻留到字符串常量池的,那如果我偏要驻留到字符串常量池呢?有没有办法呢?

有的,String类的intern方法就可以帮你完成这个事情。

以这段代码为例:

String s1 = “aa”;

String s2 = “bb”;

String str = s1 + s2;

str.intern();

在执行 str.intern(); 之前,内存图是这样的:

Java中,那些关于String和字符串常量池你需知道的东西

在执行 str.intern(); 之后,内存图是这样的:

Java中,那些关于String和字符串常量池你需知道的东西

intern方法就是创建了一个HashtableEntry对象,并把value指向String对象,然后把HashtableEntry通过hash定位存到对应的字符串成常量池中。当然,前提是字符串常量池中原来没有对应的HashtableEntry。

没了,intern方法,就是这么简单,一句话给你说清楚了。

关于intern方法,还有一个很有趣的故事,有兴趣的可以去看一下why神的这篇文章 《深入理解Java虚拟机》第2版挖的坑终于在第3版中被R大填平了

编译优化

====================================================================

写到这里,好像只有一个坑没有填。就是这个题为什么输出的是true。

final String s3 = “cc”;

final String s4 = “dd”;

String str3 = s3 + s4;

String str4 = “ccdd”;

//为什么输出的是true呢???

System.out.println(str3 == str4);

这道题和上面那道题相比,有点相似,在原来的基础上加了两个final关键字。我们先看一下这段代码的字节码:

Java中,那些关于String和字符串常量池你需知道的东西

Java中,那些关于String和字符串常量池你需知道的东西

又是一段字节码指令,不需要看懂,你点一下#4,居然就可以看到“ccdd”字符串。

原来,用final修饰后,JDK的编译器会识别优化,会把 String str3 = s3 + s4; 优化成String str3 = “ccdd” 。

image.png

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!

某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
“zoom: 33%;” />

总结

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!

某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人!

[外链图片转存中…(img-jWmOsawu-1713288232621)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值