字符串常量池(String Table)

简介: 字符串常量池(String Table)

在以不同的创建方式创建字符串的时候引发的思考:

1. public class Test1 {
2. public static void main(String[] args) {
3.         String str1="hello";
4.         String str2="hello";
5.         String str3=new String("world");
6.         String str4=new String("world");
7.         System.out.println(str1==str2);
8.         System.out.println(str1==str3);
9.         System.out.println(str3==str4);
10.     }
11. }

思考:为什么以字符常量串创建的时候,创建的字符串的地址一样,而用new String的时候结果的地址是不一样的呢?也就是为什么str1和str2引用的对象是一样的,而str3和str4不一样。

为了使程序的运行速度更快、更节省内存,Java为8种基本数据类型和String类都提供了常量池

为了节省存储空间以及程序的运行效率,Java中引入了:

1. class文件常量池:每个.Java源文件编译后生成.class文件中会保存当前类中的字面常量以及符号信息

2. 运行时常量池:在.class文件被加载时,.class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份

3. 字符串常量池

字符串常量池(StringTable)

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

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

再谈String对象创建

当直接用字符串常量进行赋值的时候:

1. public static void main(String[] args) {
2. String s1 = "hello";
3. String s2 = "hello";
4. System.out.println(s1 == s2); // true
5. }

当字节码文件加载的时候字符串hello已经创建好了并且保存在字符串常量池中,当使用常量串赋值的时候优先从字符串常量池找,找到了就将该字符串引用赋值给要赋值的对象。 所以会出现这两个字符串地址一致的情况。

当我们用new来创建String对象的时候,由于new的对象是唯一的,所以地址都是不一样的。

使用常量串创建String类型对象的效率更高,而且更节省空间。用户也可以将创建的字符串对象通过intern方式添加进字符串常量池中。

intern方法

intern是一个native方法(Native方法指:底层使用C++实现的,看不到其实现的源代码),该方法的作用是手动将创建的String对象添加到常量池中。

1. public static void main(String[] args) {
2. char[] ch = new char[]{'a', 'b', 'c'};
3. String s1 = new String(ch); // s1对象并不在常量池中
4. //s1.intern(); // s1.intern();调用之后,会将s1对象的引用放入到常量池中
5. String s2 = "abc"; // "abc" 在常量池中存在了,s2创建时直接用常量池中"abc"的引用
6. System.out.println(s1 == s2);
7. } /
8. / 输出false
9. // 将上述方法打开之后,就会输出true

字符串的不可变性

String是一种不可变对象. 字符串中的内容是不可改变。字符串不可被修改,是因为:

1. String类在设计时就是不可改变的,String类实现描述中已经说明了

String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:

1. String类被final修饰,表明该类不能被继承

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

2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象

比如 replace 方法:

网上有些人说:字符串不可变是因为其内部保存字符的数组被final修饰了,因此不能改变。这种说法是错误的,不是因为String类自身,或者其内部value被final修饰而不能被修改。final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。

如果我们想修改字符串建议尽量使用StringBuffer或者StringBuilder

注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。


相关文章
|
3月前
|
安全 Java API
【Java字符串操作秘籍】StringBuffer与StringBuilder的终极对决!
【8月更文挑战第25天】在Java中处理字符串时,经常需要修改字符串,但由于`String`对象的不可变性,频繁修改会导致内存浪费和性能下降。为此,Java提供了`StringBuffer`和`StringBuilder`两个类来操作可变字符串序列。`StringBuffer`是线程安全的,适用于多线程环境,但性能略低;`StringBuilder`非线程安全,但在单线程环境中性能更优。两者基本用法相似,通过`append`等方法构建和修改字符串。
59 1
|
18天前
|
NoSQL Redis
Redis 字符串(String)
10月更文挑战第16天
30 4
|
29天前
|
canal 安全 索引
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
33 5
|
1月前
|
存储 JavaScript 前端开发
JavaScript 字符串(String) 对象
JavaScript 字符串(String) 对象
40 3
|
3月前
|
NoSQL 安全 Java
Redis6入门到实战------ 三、常用五大数据类型(字符串 String)
这篇文章深入探讨了Redis中的String数据类型,包括键操作的命令、String类型的命令使用,以及String在Redis中的内部数据结构实现。
Redis6入门到实战------ 三、常用五大数据类型(字符串 String)
|
2月前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
3月前
|
C# 开发者 UED
WPF开发者必备秘籍:深度解析文件对话框使用技巧,打开与保存文件原来如此简单!
【8月更文挑战第31天】在WPF应用开发中,文件操作是常见需求。本文详细介绍了如何利用`Microsoft.Win32`命名空间下的`OpenFileDialog`和`SaveFileDialog`类来正确实现文件打开与保存功能。通过示例代码展示了如何设置文件过滤器、初始目录等属性,并使用对话框进行文件读写操作。正确使用文件对话框能显著提升用户体验,使应用更友好易用。
68 0
|
3月前
|
API C# 开发者
WPF图形绘制大师指南:GDI+与Direct2D完美融合,带你玩转高性能图形处理秘籍!
【8月更文挑战第31天】GDI+与Direct2D的结合为WPF图形绘制提供了强大的工具集。通过合理地使用这两种技术,开发者可以创造出性能优异且视觉效果丰富的WPF应用程序。在实际应用中,开发者应根据项目需求和技术背景,权衡利弊,选择最合适的技术方案。
128 0
|
3月前
|
存储 JSON NoSQL
揭秘Redis字符串String的隐藏技能!从基础到进阶,让你的数据存储操作秒变高大上!
【8月更文挑战第24天】Redis中的字符串类型作为其基石,不仅能够存储从简单文本到复杂格式如JSON的各种数据,还能通过多样化的命令实现包括但不限于自增自减、设置过期时间等高级功能,极大提升了其实用性和灵活性。例如,使用`SET`命令可添加或更新键值对,`GET`获取值,`DEL`删除键;同时,`INCR`和`DECR`支持对整数值的原子性增减操作,非常适合实现计数器等功能;通过`EXPIRE`命令设置过期时间,则适用于需要限时存储的应用场景。尽管名为“字符串”,但实际上还可存储图片、音频文件的Base64编码等形式的数据,为开发者提供了强大而灵活的工具。
47 0
|
3月前
|
存储 Java