String/StringBuilder/ToString()底层代码解析( JAVA / C# )

前言

本章笔记直接记录的string、StringBuilder内存存储原理,并没有大幅度、重点的去介绍堆、栈、常量池的相关底层实现原理。
所以,为了帮助大家更好的去理解,可以在阅读本文章前去了解堆、栈、常量池之间的基础关系,对以下的阅读有很大的帮助。

名词解释

栈:存放变量(值类型)

堆:存放对象(引用类型)

常量池:它是一个Hash表。
为了提升性能和减少内存开销,避免字符串的重复创建,所以开辟出来一个单独的内存空间,就是字符串池。
字符串常量池是由String类私有的维护。

八大基本类型: byte、short、int、long、boolean、float、double、char
四大引用类型:数组、class、interface、字符串(string


一、String


1.1 示例

案例一: 不同变量赋值( = )

String  str1 = "Hello" ;
String  str2 = "Hello" ;
System.out.println(str1 );  //结果: Hello
System.out.println(str1 );  //结果: Hello
  • 情况1:不存在
    1. 变量 str1 会存放中,
    2. 首先在常量池中进行查找“Hello”是否存在。
    3. 不存在时,会在常量池中以键值对格式创建<key,value>,value则指向堆中的“Hello”对象指针。
    4. 从而,str1的引用地址就是堆中0x0001对应的“Hello”指针
  • 情况2:存在
    1. 变量 str2 会存放中,
    2. 首先在常量池中进行查找“Hello”是否存在。
    3. 存在时,会继续使用常量池中已存在的key,不会再新建。也就是 str2使用常量池中hello指向堆中的0x0001对应的“Hello”指针
    4. 与str1的引用地址是一个。

在这里插入图片描述

案例二:相同变量赋值( = )

我们都知道String属于类,它是不可变的。即一旦一个String对象被创建以后便不能被更改、变长、修改;直至这个对象被销毁。

不可变 : 文章下方会有专门的讲解

下面写了一个小例子,如下方所示:

String  str1 = "Hello" ;
str1 = "Word" ;
//打印出来的str1为: Word
System.out.println(str1);  

看到这里,可能就会有疑问:不是不能被修改吗?怎么会对他进行了修改?
针对这个问题,我画了一张底层实现原理图,希望能够帮助到大家。如图所示:
在这里插入图片描述
前面我们说到了第一次str1赋值“Hello”,在常量池中创建后,其value指向对象指针

从图中可以看出,再次给str1赋值“Word”时,并不是原来堆中的实例对象进行重新赋值,而是生成一个新的实例对象,并且str1的引用地址变指向了这个新的“Word”这个字符串。

之前的实例对象“Hello”依然存在,只是不再被引用了而已;如果没有被再次引用,则会被垃圾回收。
但是吧,在这里暂时不回被回收(垃圾回收不能释放被Hash表中引用的字符串,因为Hash表中正在容纳对他们的引用。除非进程终止)

案例三:变量追加赋值( += )

前面提到了String属于类,它是不可变的。即一旦一个String对象被创建以后便不能被更改、变长、修改;直至这个对象被销毁。

小案例:

String  str1 = "Hello" ;
str1 + = " Word" ;
//打印出来的str1为:Hello  Word
System.out.println(str1)

看到这里,可能就会有疑问:不是长度不可以变吗?为什么变量str1的长度会增加?会被修改?
针对这个问题,我又画了一张底层实现原理图,希望能够帮助到大家。如图所示:
在这里插入图片描述
公式变化:
str1在追加赋值+=“Word”时,(图中为了好看,堆地址0x0003对应的字符串对象我添加了空格,不要被误导了哈~)实际上是str1+“Word”(str1指向Hello) Hello+Word " HelloWord "。

底层变化:

  1. str1 中的“Hello”、“Word” 在常量池中没有,所以需要在常量池中创建对应key,其value(0x0001、0x0002)指向堆中的对象指针(Hello、Word)。
  2. 前面也提到了,实际上str1 = str1+“Word”,而这一步操作是隐式操作,不走字符串常量池的。
  3. 也就是说:Hello+Word 是在堆中相加的,生成了新的对象
  4. 其新生成堆地址0x0003(对象在堆中地址)会赋给str1。
  5. str1 根据这个地址去找到对象 “ HelloWord ”。(之前的实例对象“Hello”“Word”依然存在,只是不再被引用了而已)。



1.2 常量池扩展(IsInterned、ReferenceEquals)

有关常量池动态机制在此处查看: string常量池/驻留池——动态机制
这两个都是在C#中提供的方法,java的我还没有使用过。之后的java练习中找到了平替将会及时补充本文哈。

Object.ReferenceEquals() 确定实例是否为同一实例

ReferenceEquals 方法是 Object类的静态方法 ,不能被改写。该方法可以比较两个引用类型的引用是否指向用一个实例。

案例一:
> string aa = "Hello";
> string bb = "Hello";  
> aa == bb     
//判断变量是否相等。返回结果为:true
 true
 
案例二: 
> object.ReferenceEquals(aa,bb)
 //判断两个变量引用地址是否相同
 t
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

元气小羊.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值