Java字符串常量池详解(StringTable)

article/2025/9/23 22:28:35

前言:在介绍字符串常量池之前,我们先来简单了解下Java中字符串的概念以及常见的一些问题.

参考书籍: 《Java核心技术》

参考网站:牛客

作者水平很有限,如果发现错误,麻烦及时告知作者哦!十分感谢!

目录

一、字符串

1.1 子串

1.2 拼接

1.2.1 字符串与字符串拼接

1.2.2 字符串与非字符串拼接

1.3 字符串的不可变性

1.3.1为什么设计成不可变的 

1.4 判断字符串是否相等

二、字符串常量池

2.1 字符串常量池的应用

2.1.1 再谈String对象创建

 三、面试题

一、字符串

Java字符串其实只是Unicode字符序列,Java本身没有内置的字符串类型,而是在标准Java类库中提供了一个预定类,很自然的叫做String,每一个双引号引起来的字符串都是String类的一个实例:

        String s = "";//空的字符串String str = "Hello";

1.1 子串

提到子串,那就免不了介绍以下subString这个方法了,该方法的功能为:从一个较大的字符串中提取一个子串。

其一为,subString(x,y):(注意这里是左闭右开区间) 所以该方法是截取下标x~y-1 的字符串。

其二为,subString(x) : 该方法是截去前x个字符串后的字符串。

具体情况如下代码:

ps:subString 的工作方式有一个优点,容易计算子串的长度,比如:字符串s.subString(a,b) 的长度就为b-a。 

1.2 拼接

1.2.1 字符串与字符串拼接

与绝大多数设计语言一样,Java语言允许使用+号来连接两个字符串。

下述代码将“hello”赋值给了s3变量:

1.2.2 字符串与非字符串拼接

当将一个字符串与非字符串的值进行拼接的时候,后者会转换成字符串,例如:

 此特性其实我们经常在打印时候用到:

注意:这里是字符串与非字符拼接并不是字符与非字符拼接:

如果是后者的话;可能会转变成数字,例:

1.3 字符串的不可变性

String类中没有提供修改字符串中某个字符的方法,如果希望将”hello“修改为“help!” ,只能提取想要保存的字符串,再与希望替换的字符串拼接,比如:

分析:

由于不能修改Java字符串中的某个字符,所以Java我、文档中将String类称之为不可变的,不过可以修改s指向的值,让它引用另外的字符串。 

可能有人会说,拼接字符串这样的做法是否会降低运行效率呢?

答案其实并不确定,虽然通过拼接的方法,让s2指向了"help!"看上去有些低效,但是不可变字符串却有一个优点:编译器可以让字符串共享。

原因:

 通过观察源码可知,String类的value是被private修饰的,类外是拿不到这个值的

 可能很多人以为String类不可变是因为value[] 这个数组被final修饰,但其实并不是这样的,因为value存储的是一个引用,而不是常量。

下面用代码诠释一下:

我们发现,arr[1]的值的确被改为了c。准确的说 这里的final限制的是arr指向的值不能改变,也就是不能重新指向一个新的数组对象。

 总结:

  1. String类被final修饰,表明该类不能被继承
  2. value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。

所以一些设计字符串修改的函数,都是创建一个新的对象,然后修改新的对象。 

注意  += 这种的拼接操作,也不是在String类自身上进行的,而是通过中间创建许多临时变量而完成的,效率很低下。于是引入了StringBuffer和StringBuilder。

 以下代码可以看出String类在拼接时的效率比StringBuffer和StringBuilder低:

public static void main(String[] args) {long start = System.currentTimeMillis();String s = "";for(int i = 0; i < 1_0000; ++i){s += i;}long end = System.currentTimeMillis();System.out.println(end - start);start = System.currentTimeMillis();StringBuffer sbf = new StringBuffer("");for(int i = 0; i < 1_0000; ++i){sbf.append(i);}end = System.currentTimeMillis();System.out.println(end - start);start = System.currentTimeMillis();StringBuilder sbd = new StringBuilder();for(int i = 0; i < 1_0000; ++i){sbd.append(i);}end = System.currentTimeMillis();System.out.println(end - start);}

1.3.1为什么设计成不可变的 

为什么 String 要涉及成不可变的?(不可变对象的好处是什么?) 

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了.
  2. 不可变对象是线程安全的.
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中.

1.4 判断字符串是否相等

可以使用equals方法来检测字符串是否相等,形如:s.equals(r); 如果字符串s与字符串r相等,则返回true,否则返回false。

需要注意的是:s与t可以是字符串常量,也可以是字符串字面量(用双引号引起来的部分)。

比如以下的语句也是合法的:

🔔🔔千万不要使用 == 来判断两个字符串是否相等,这个运算符只能确认两个字符串是否处在同一位置,虽然,字符串在同一位置上,它们必然相等, 但是完全可能将内容相同的多个字符串放在不同的位置上。

例如:

如果虚拟机始终将相同的字符串共享,就可以使用 == 运算符来检测是否相等,但实际上字符串字面量是共享的,而+ 或 substring 等操作得到的字符串并不共享,因此千万不能使用==来判断字符串的相等性,以免出现bug。 

二、字符串常量池

字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(一种高效用来进行查找的数据结构,后序给大家详细介绍),不同JDK版本下字符串常量池的位置以及默认大小是不同的:

JDK版本字符串常量池位置大小设置
Java6(方法区)永久代固定大小:1009
Java7堆中可设置,没有大小限制,默认大小:60013
Java8堆中可设置,有范围限制,最小是1009

2.1 字符串常量池的应用

2.1.1 再谈String对象创建

 大家可能会好奇,为什么s1,s2指向的是同一个对象,而s3,s4指向的对象却不相同呢?

在Java程序中,类似于:1, 2, 3,3.14,“hello”等字面类型的常量经常频繁使用,为了使程序的运行速度更快、更节省内存,提供了一个String类型的常量池,只要是双引号引起来的对象,都会存放在常量池中。

 分析:因为s1,s2分别都是被双引号引起来的对象,所以都会被存储到常量池中,可能有人会问,为什么s2并没有单独在常量池中存储一份而是引用了s1所指向的对象呢?

; 那是因为被引号引起来的对象,会先到常量池中寻找是否已经存在相同的内容,如果存在相同内容,那么就直接引用它,就不需要再创建一个对象了(这也是为什么常量池效率高的原因)。

现在我们再来看几组代码;

代码1;

 我们可以发现,s1,s2,s3都引用了同一个数组对象,这里s3不与s1,s2相同的原因是因为,s3是通过new了一个String对象,而String对象里面的value值存储的是hello的地址值。

 代码2:

 下面难度加大一点:

小插曲:

这里需要注意的是s1的new string对象中存放的是数组,根据源码我们可以得知,是将这个ch数组拷贝一份再存入string对象中的。

 但是我们只要加入intern这个方法,就可以让其相等。

原因:intern 是一个native方法(Native方法指,底层使用C++实现的,看不到其实现的源代码)
:该方法会检查常量池中是否有这个对象,如果无,则入池(入池并不会将自身销毁,而是在常量池中添加这个对象),有的话,则返回该对象(并不会再次入池了)。

如果将这个顺序一调换,就输出false。

原因也很简单,因为已经在常量池中存在这个”abc“了,不会再将s1的string对象的abc再次入池。

 三、面试题

 面试题:请解释String类中两种对象实例化的区别
JDK1.8中(该题的前提是:常量池不存在hello)
1. String str = "hello"
只会开辟一块堆内存空间,保存在字符串常量池中,然后str共享常量池中的String对象
2. String str = new String("hello")
会开辟两块堆内存空间,字符串"hello"保存在字符串常量池中,然后用常量池中的String对象给新开辟
的String对象赋值。
3. String str = new String(new char[]{'h', 'e', 'l', 'l', 'o'})
现在堆上创建一个String对象,然后利用copyof将重新开辟数组空间,将参数字符串数组中内容拷贝到
String对象中


http://chatgpt.dhexx.cn/article/XcFSkFxi.shtml

相关文章

java常量池在哪里_java常量池在哪?有什么用处?

为了更方便的使用对象&#xff0c;常量池是我们需要了解的必要一环&#xff0c;下面来看看常量的用处及它的存放地点。 Java常量池存放地点在哪? 如图&#xff1a; 在Java6和6之前&#xff0c;常量池一般是存放在方法区中的&#xff0c;到了Java7&#xff0c;常量池就被存放到…

Java常量池理解

Java常量池理解 常量池分为两种&#xff1a;静态常量池和运行时常量池。 静态常量池 每个类在编译之后都会生成class文件&#xff0c;而class文件中就包含有静态常量池&#xff0c;分析class文件&#xff0c;如下图所示&#xff1a; 由于常量池中的常量的数量不是固定的&…

Java 常量池详解(二)class文件常量池 和 Java 常量池详解(三)class运行时常量池

Java 常量池详解&#xff08;一&#xff09;字符串常量池 2.class文件常量池&#xff08;class constant pool&#xff09; 产生时机&#xff1a;当java文件被编译成class文件之后&#xff0c;就会生成class常量池&#xff0c;跟jvm 无关系 常量池主要存放两大类常量&#xff…

java 查看类常量池_Java中常量以及常量池

1、举例说明 变量 常量 字面量 1 int a=10;2 float b=1.234f;3 String c="abc";4 final long d=10L; a,b,c为变量,d为常量 两者都是左值;10,1.234f,"abc",10L都是字面量; 2、常量池: 常量池专门用来用来存放常量的内存区域,常量池分为:静态常量池…

一文解析Java常量池、静态常量池、运行时常量池和字符串常量池的区别与联系

Java常量池关系图 Java常量池 Java常量池是Java编译器在编译Java源代码时&#xff0c;为了优化性能和节省空间所创建的一种常量缓存机制。它包含了所有的基本数据类型、字符串常量、符号引用等常量&#xff0c;这些常量都是在编译期被确定下来的&#xff0c;并被存储在.class文…

java常量池总结

java常量池 1.class常量池2.运行时常量池3.基本类型包装类常量池4.字符串常量池 1.class常量池 在JAVA中&#xff0c;Java类&#xff08;.java&#xff09;文件被编译后就会形成一份class文件&#xff1b;class文件中除了包含类的版本、字段、方法、接口等描述信息外&#xff…

java号码池_Java常量池详解

jvm虚拟内存分布图&#xff1a; 程序计数器&#xff1a;JVM执行程序的流水线。 本地方法栈&#xff1a;JVM调用操作系统方法所使用的栈。 虚拟机栈&#xff1a;JVM执行Java代码所使用的栈。 方法区&#xff1a;存放一些常量、静态变量、类信息等&#xff1b;可以理解为class文件…

java常量池在哪里_【Java基础】Java常量池在哪里? - 收获啦

1.java常量池的介绍 java中的常量池&#xff0c;通常指的是运行时常量池&#xff0c;它是方法区的一部分&#xff0c;一个jvm实例只有一个运行常量池&#xff0c;各线程间共享该运行常量池。 java常量池简介&#xff1a;java常量池中保存了一份在编译期间就已确定的数据。它里面…

java静态池_java 常量池静态变量详解

Java中的常量池&#xff0c;实际上分为两种形态&#xff1a;静态常量池和运行时常量池。 所谓静态常量池&#xff0c;即*.class文件中的常量池&#xff0c;class文件中的常量池不仅仅包含字符串(数字)字面量&#xff0c;还包含类、方法的信息&#xff0c;占用class文件绝大部分…

java常量池在哪里_Java常量池详细说明

java常量池技术 java中的常量池技术&#xff0c;是为了方便快捷地创建某些对象而出现的&#xff0c;当需要一个对象时&#xff0c;就可以从池中取一个出来(如果池中没有则创建一个)&#xff0c;则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间&#x…

java中常量池存的是什么_Java中常量池是什么?Java常量池的介绍

本篇文章给大家带来的内容是关于Java中常量池是什么?Java常量池的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区。 程序计数器是jvm执行程序的…

java long常量池_Java-常量池

Java-常量池 常量池是类文件中最复杂的数据结构。对于JVM字节码来说&#xff0c;如果操作数是很常用的数字&#xff0c;比如 0&#xff0c;这些操作数是内嵌到字节码中的。如果是字符串常量和较大的整数等&#xff0c;Class文件则会把这些操作数存储到常量池中&#xff0c;当使…

Java常量池原理以及垃圾回收

Java常量池 常量池&#xff1a;用于存放编译期间生成的各种字面量和符号引用 字面量&#xff1a;由字母数字等构成的字符串或数值常量&#xff0c;如int a 1中 1就是字面量 符号引用&#xff1a;编译原理中的概念&#xff1b;是相对直接引用来说的&#xff0c;主要包括三类常…

Java 常量池详解(一)字符串常量池

在Java的内存分配中&#xff0c;总共3种常量池&#xff1a; Java 常量池详解&#xff08;二&#xff09;class文件常量池 和 Java 常量池详解&#xff08;三&#xff09;class运行时常量池 1.字符串常量池(String Constant Pool&#xff09; 在JDK1.7之前运行时常量池逻辑包含…

Java常量池储存什么_JAVA常量池中存储的常量是什么

展开全部 我当初也存在这样一个疑问&#xff0c;下面我把e69da5e887aa3231313335323631343130323136353331333262356165当初所搜集的一些资料以及自己的理解贴出来给你看看(比较多&#xff0c;需要耐心点看&#xff0c;呵呵)&#xff1a; 理解Java常量池 JVM运行时数据区的内存…

深度剖析Java常量池

Class常量池 class常量池可以理解为是Class文件中的资源仓库。Class文件中除了包含类的版本、字段、方法、接口等描述信息外&#xff0c;还有一项信息就是常量池(constant pool table)&#xff0c;用于存放编译期生成的各种字面量和符号引用。 一个Class文件的16进制大体结构如…

Java常量池

Java常量池 一.相关知识 1.何为常量 第一种常量&#xff1a;是一个值&#xff0c;我们将这个值本身称为常量。比如&#xff1a; 整型常量&#xff1a;1024 实型常量&#xff1a;1.024 字符常量&#xff1a;g c w 字符串常量&#xff1a;"gcw" 逻辑常量&#xff1a;t…

Java 常量池

常量池分为 Class 常量池常量池、运行时常量池、字符串常量池。 1、 Class 常量池常量池&#xff08;静态常量池&#xff09; Java 文件被编译成 Class 文件&#xff0c;Class 文件中除了包含类的版本、字段、方法、接口等描述信息外&#xff0c;还有一项就是 Class 常量池&am…

JAVA常量池,一篇文章就足够入门了。(含图解)

前言 一直在《深入理解JVM》对常量池只有一个浅薄的了解&#xff0c;之前也遇到过这种题目&#xff0c;今天还是要挑出来进行一次全方位的了解。 常量池分类 常量池大体可以分为&#xff1a;静态常量池&#xff0c;运行时常量池。 静态常量池 存在于class文件中&#xff0c…

基于Elman神经网络的刀具剩余使用寿命预测(初学者+matlab代码实现)

1.Elman介绍 Elman神经网络是 J. L. Elman于1990年首先针对语音处理问题而提出来的&#xff0c;是一种典型的局部回归网络( global feed forward local recurrent)。Elman网络可以看作是一个具有局部记忆单元和局部反馈连接的递归神经网络 它的主要结构是前馈连接, 包括输入层、…