让你对“字符串常量池“有新的认知

让你对字符串常量池有新的认知

前言😏

最近小编接触到java中的字符串,对于经受了c指针或者数组去操作字符串的折磨后,接触到java中的字符串,不得不佩服java中字符串的设计者(五体投地的那种)。当让相信小伙伴也看到了标题,我们今天来一起研究一下字符串常量池!

img

字符串常量池的定义及功能💕

首先我们来看到这个名词的时候,首先我们能想到什么了?常量,顾名思义,就是不可变,而字符串所为一个对象,需要由String这个类来创造,那么顺藤摸瓜,String这个类就是不可变的,那什么叫做不可变类了?要实现一个不可变类,需要做到4点:

  1. 首先要保证类被final关键字修饰,避免被其它类继承;
  2. 保证成员变量要被final修饰,做到一次初始化,终生不变;
  3. 不要提供任何的setter方法。
  4. 当对这个类的对象进行一系列的操作后,一定得返回这个对象得副本,而不是原来得对象。

聪明的宝宝已经注意到了,我们大多数对于字符串的操作都会返回一个新的对象,会在堆(heap)上开辟大量的空间,虽然当对象的引用(存在于栈区)随着它所在的栈帧被回收,堆区的对象也会被回收,java还是对于性能还是不太满意。那么怎么个办了?既然避免不了操作过程中的新的字符串的对象的产生,那么我们聪明的设计者打算从源头上出发——字符串常量池!我来稍微解释一下它的存在目的:我们知道我们创建一个对象需要用到new关键字(你有对象吗?没有的话,就new一个把!❤️),而一个new就势必会在堆区上进行对象的创造,无论字符串的内容是否相同,记住这句话,这是理解字符串常量池的关键。假如我们写了100个new String(“靓仔靓女!”),没有字符串常量池,那么就会在堆区上创建100个对象,是不是显的很呆的那种。那么重点来了,字符串常量池是怎么工作的了?new String(“靓仔靓女!”)会先去字符串常量池看看有没有“靓仔靓女!”这个对象,如果没找到,首先会在字符串常量池创建这个对象,然后就去堆区创建这个对象,返回的是堆区的对象,new还是比较专一的,只会返回堆区的对象。当我们第二个new String(“靓仔靓女!”)执行的时候,也会来到字符串常量池来耍耍,发现有“靓仔靓女”这个对象了,它就不创建了,但是它也是专一的,也会跑到堆区去创建一个新的对象,后面的98个同的二哥的操作。看到这里,后很多小伙伴就按耐不住了,那要着字符串常量池有何用?别着急,我们再 来聊一种新的创建对象的方式——“”,没错那就是双引号,这种创建对象的方式,首先回去常量池看看有没有“靓仔靓女!”,如果没有的话,会在常量池创建这个对象,然后返回这个对象;如果有靓仔靓女!这个对象,返回的就是这个已经存在于常量池中的对象,不会创建任何的对象!这样就大大的节省了空间。

总结起来就是一句话:new 的⽅式始终会创建⼀个对象,不管字符串的内容是否已经存在,⽽双引号的⽅式会
重复利⽤字符串常量池中已经存在的对象。所以,以后创建字符串对象的时候,这个“”你对它的理解还只停留在表面吗?

**字符串常量池存在于什么地方了?**😪

在java8之前,常量池存在于永久代中,也就是我们所说的方法区,方法区可以理解为一个接口,永久代实现了这个接口,在java8以后,移除了永久代,常量池流浪到堆区,堆区收留了它。而原来的永久代取而代之的是元空间,这两者的区别就是前者存在于运行时数据区,所以大小收到jvm本身大小的限制,而元空间则是直接放在内存中的,谁大谁小,就不用多说了吧。

字符串放到常量池的方法🤯

根据前文提到的,无论我们使用“”或者new关键字去创建对象,都会在常量池创建相应的对象,那么我们是否有可能有一种对象只存在于堆区而不存在于常量池的了?思考一下,心中有答案再往下看哦,无论对于错,你或许会有更深的印象!来看这样的代码:

String s1 = new String("靓仔") + new String("靓女");

这段代码在堆栈中究竟有怎样的爱恨情仇?首先:new String(“靓仔”)和new String(“靓女”)会先在常量池创建“靓仔”和“靓女”这两个对象,然后跑去堆区搞事情,同样创建“靓仔”和“靓女”两个对象,相信看到这里的读者,一定能够理解我所解释到的。那么我们接下来看到了什么?没错,就是“+”号,字符串中的“+”号其实是一种语法糖(字符串拼接),就是为了我们便于操作!还记得我前面提到的字符串的任何改变的操作都会返会新的对象,这也不例外!那么背后的原理是什么了?为了装出一丢丢的专业感,我们去反编译一下字节码来看一下吧:image-20220802000226607

聪明如你,看懂了吗?有时候尝试阅读一下反编译后的字节码,我们能搞清楚背后真正的原理,+号其实就是StringBuilder的语法糖,也就是说我们每一次使用+号就会在堆区创建对象,所以,可爱的宝子们不要在循环里面去用+号拼接字符串,也得多亏堆区“脾气”好啊,不然看这傻逼代码,直接罢工有木有啊有木有!

扯远了扯远了,说到哪里了?哦,我是来说字符串放到常量池的方法的,梳理一下,目前常量池有“靓仔”和“靓女”两个对象,堆区有“靓仔”,“靓女”和+号拼接返回的对象“靓仔靓女”!发现常量池没有“常量池”没有“靓仔靓女”这个对象,我们需要用到String类提供的String.intern() 方法。这个方法的实现原理在常量池还没有搬家的时候,执⾏String.intern() ⽅法的时候,不管对象在堆中是否已经创建,字符串常量池中仍然会创建⼀个内容完全相同的新对象;搬家之后:由于字符串常量池放在了堆中,执⾏String.intern() ⽅法的时候,如果对象在堆中已经创建了,字符串常量池中就不需要再创建新的对象了,⽽是直接保存堆中对象的引⽤,也就节省了⼀部分的内存空间。看下面的例子:相信你一定能理解:

image-20220802002347134

总结😊

好了,今天的讲解就到这里了,由于小编水平有限,文中可能存在描述偏差甚至错误,希望火眼金金的你能够在评论区留下你宝贵的意见,供大家学习交流,最后的最后,一键三连有木有啊有木有,铁汁!!!😍😍😍😍

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WindFall1314

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

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

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

打赏作者

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

抵扣说明:

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

余额充值