字符串常量池StringTable

字符串常量池

String类的内部结构

我们先看一下String类的源码

在这里插入图片描述

从源码我们可以看出,String类对象有两个属性,分别是一个用于保存字符串值的字符数组,一个是当前字符串的hash值。也就是说加入我们new出一个String对象,它在内存上应该是这样的
在这里插入图片描述

String对象创建的方式

String类对象的创建方式主要有三种,分别是直接使用字符串常量构建、把字符串常量当做构造器的参数创建,把字符数组当做构造器的参数构建。

直接使用字符串常量构建

String str = "hello";

把字符串常量当做构造器的参数创建

String str = new String("hello");

把字符数组当做构造器的参数构建

char[] chars = {'h', 'e', 'l', 'l', 'o'};
String str = new String(chars);

引出问题

下面我们分别以三种方式创建同一种字符串,在通过 == 判断其是否相等(通过 == 去判断两个对象是否相等其实判断的是两个对象的地址)

在这里插入图片描述

按常理说三个输出语句应该全输出false,但是第一个却输出为true。那这到底是为什么呢?其实这是字符串常量池捣的鬼。

字符串常量池

字符串常量池(String Pool)是Java中的一块特殊的内存区域,用于存储字符串常量。它是在堆内存中的一部分,用于提高字符串的重复利用和节省内存空间。

在Java中,字符串常量是指直接以字面值形式出现的字符串,例如:“hello”、"world"等。当使用字符串常量创建字符串对象时,如果字符串常量池中已经存在相同内容的字符串,则直接返回常量池中的引用,而不会创建新的对象;如果字符串常量池中不存在相同内容的字符串,则创建一个新的字符串对象并将其添加到字符串常量池中。

底层数据结构

字符串常量池底层数据结构是一个被称为StringTable的哈希表,哈希表是一个数组,数组中每个元素都是一个头结点,每个头结点都指向一个单链表,单链表中的每个结点的包含三个部分:hashcode、String对象的引用、下一个节点的引用。字符串常量池在堆中的结构如下图所示:
在这里插入图片描述

如何添加一个字符串对象在常量池中

当一个字符串对象要插入常量池中时,首先会根据字符串的值计算出它的hashcode,然后根据它的hashcode计算出它应该放到哈希表中的索引,进而再与索引处链表上的每一个指向的字符串进行值的比对,如果发现存在就不会改动链表。如果不存在则会在链表上添加一个节点并创建新的字符串对象使新添节点的String部分指向新创建的字符串对象。

三种创建方式分别会对常量池造成的影响

这三种创建方式我们都分为字符串常量池中没有该字符串和有该字符串来讲解会发生什么,以下简称常量池。

1. 直接使用字符串常量构建

String str = "hello"
  1. 常量池中没有hello

在运行时,先通过字符串的hashcode检查常量池中是否有hello,如果没有则在堆中创建一个value指向hello字符数组的String对象,并把这个对象添加到常量池中,再把String类对象的地址返回给str。

  1. 常量池中有hello

常量池中有的话就直接把常量池中hello的String类对象的地址返回给str。
初始化好str的内存图

2. 把字符串常量当做构造器的参数创建

String str = new String("hello");
  1. 常量池中没有hello

    先通过字符串hashcod判断常量池中有没有hello,没有的话就直接在堆上创建一个String类对象,对象的value属性指向hello字符数组

在这里插入图片描述

  1. 常量池中有hello

    有的话仍然会新创建一个String类对象,并使其value指向已创建好的字符数组hello。

在这里插入图片描述
这里的图虽然与上一个图看着相同,但是创建的步骤是不同的,上一个图中是先在常量池中创建String类对象,然后再在堆中创建一个String类对象。而这个是直接把常量池中已存在的字符数组用于初始化新创建的类对象的value属性,也就是比上一个少一个把字符串加入到常量池中的步骤。

3. 把字符数组当做构造器的参数构建

char[] chars = {'h', 'e', 'l', 'l', 'o'};
String str = new String(chars);

对于这种情况,无论常量池中有没有hello都会先拷贝一份hello数组,再新创建一个String类对象使其value属性指向hello字符数组,然后把String类对象的地址返回给str,不会先看常量池中有没有hello。
在这里插入图片描述
了解了常量池的原理后上面引出的问题就很容易解释
在这里插入图片描述

这里之所以str1==会相等是因为str1和str2引用的都是常量池中的同一个String类对象,而 == 比较的是两个对象的地址,所以比较结果为true。下面的str1 == str3和str1= =str4之所以结果为false是因为即使常量池中已有hello类对象,str3和str4 使用new的创建方式仍然会在堆上创建新的类对象,只是str3的value引用的是常量池里的而str4不是。
在这里插入图片描述

inter方法

intern方法是String类的方法,它可以使当前不在常量池中的String类对象加入到常量池中。
在这里插入图片描述
对于这个程序我们发现str1与str2的value指向的不是同一个字符数组,但是当我们使用s1.intern方法时就会使str1的对象加入到常量池中,进而str2创建时其value属性就会引用常量池中的字符数组导致str1的value与str2的value相同。
在这里插入图片描述
下面是加入intern前和加入intern后内存上的区别
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值