再谈String

1、字符串常量池

1.1 创建对象的思考

下面是两种创建字符串对象的代码

java 复制代码
    public static void main1(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1 == s2);//true
        String s3 = new String("hello");
        String s4 = new String("hello");
        System.out.println(s1 == s3);//false
        System.out.println(s3 == s4);//false
    }

上述程序创建方式类似,但为什么打印结果s1 == s2的时候是true,s1 == s3打印的时候是false,s3 == s4打印的时候是false呢?

在Java中,对于1、2、3、3.14、"hello"等字面类型的常量经常频繁使用,为了使程序运行更快,更节省内存,Java为8种基本类型和String类都提供了常量池。

"池" 是编程中的一种常见的, 重要的提升效率的方式, 我们会在未来的学习中遇到各种 "内存池", "线程池", "数 据库连接池" .... 比如:家里给大家打生活费的方式 1. 家里经济拮据,每月定时打生活费,有时可能会晚,最差情况下可能需要向家里张口要,速度慢 2. 家里有矿,一次性打一年的生活费放到银行卡中,自己随用随取,速度非常快 方式2,就是池化技术的一种示例,钱放在卡上,随用随取,效率非常高。常见的池化技术比如:数据库连接 池、线程池等。

为了节省程序存储的空间并且提高程序的运行效率,Java种引入了:

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

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

3、字符常量池:其具体概念会在讲解JVM时候详细解释。

1.2 字符串常量池

字符串常量池在Java的官方文档中并未详细说明,只是有一个在JVM中的StringTable类,实际上,是一个固定大小是HashTable(一种高效用来进行查找的数据结构),不同JDK版本下字符串常量池的位置以及默认大小是不同的:

1.3 字符串常量池的创建

如下代码,创建字符串就会先在字符串常量池先进行创建:

java 复制代码
public static void main(String[] args){
      String s1 = "hello";
      String s2 = "hello";
      System.out.println(s1 == s2);//true
}

那么创建过程是什么呢?下面通过画图进行讲解:

JVM中存在栈和堆。

当执行代码String s1 = "hello"的时候,堆中有一个StringTable类,其中是一个HashTable,在HashTable中存储的是一些链表的地址。当我们String s1 = "hello"的时候,常量池中会有一个空间来指向一个链表,这个链表会再指向一个String对象,这个String对象的value会指向一个字符数组,从而存储起来hello。

同时,栈中开辟一块空间存放0x11。 这就是一次将常量放入常量池的过程。

存储字符串常量的时候,会先检查当前的常量池中 是否存在所要存储的常量,如果有的话,就不会再放入常量池中!

我们刚刚定义了String s2 = "hello",则先会再常量池中,寻找一下,是否有"hello"的常量,检查发现有,则直接再在栈中开辟一块空间给s2,但因为s2所要存储的常量以及在常量池中了,所以s2的地址直接指向常量池中是String对象即可。

所以在打印s1==s2的时候是true。

但如果是s3的创建方式:new String("hello")。 创建结果如图所示:

即只要有new一个对象的发生,就一定会在堆中创建一个对象,则s4指向的地址为0x999的对象的地址,虽然也可以找到地址为0x88的hello,但s1和s3所存的地址就不一样了,所以在打印的时候,会出现s1 == s3的结果为false。

通过上面的例子可以看出:使用常量串创建String类型对象的效率更高,而且更节省空间。

1.4 intern方法

有如下代码:

java 复制代码
    public static void main(String[] args) {
        char[] ch = {'a','b','c'};
        String s1 = new String(ch);
        String s2 = "abc";
        System.out.println(s2 == s1);
    }

通过上面内容的学习:我们知道输出的结果一定是false。

过程如下:

char[] ch = new char[]{'a', 'b', 'c'};之后,会在堆里有一个字符数组0x87,然后ch中存储地址0x87。

然后String s1 = new String(ch); 和上面一样,new出了新是对象String。

执行String s2 = "abc"的时候,过程如下,将 a b c放入常量池中 。

此时s1 和 s2指向的地址并不相同,索引打印结果自然是false。

但如果在String s1 = new String(ch)的代码之后,添加一行代码:s1.intern(),就可以实现将s1对象的引用放入到常量池。

java 复制代码
    public static void main(String[] args) {
        char[] ch = {'a','b','c'};
        String s1 = new String(ch);
        String s2 = "abc";
        s1.intern();//1、先检查s1所指向的对象是否在常量池2、有则不入池,没有则入池
        System.out.println(s2 == s1);
    }

如果加上了这行代码,过程就会如下图所示,结果返回true: intern方法:

1.会先检查调用该方法所指的对象是否在常量池中存在。

2.如果有则不入池。

3.如果没有则入池。

相关推荐
繁依Fanyi7 分钟前
ColorAid —— 一个面向设计师的色盲模拟工具开发记
开发语言·前端·vue.js·编辑器·codebuddy首席试玩官
易只轻松熊24 分钟前
C++(23):容器类<vector>
开发语言·数据结构·c++
Lu Yao_1 小时前
用golang实现二叉搜索树(BST)
开发语言·数据结构·golang
沐土Arvin1 小时前
前端图片上传组件实战:从动态销毁Input到全屏预览的全功能实现
开发语言·前端·javascript
找不到、了1 小时前
Spring-Beans的生命周期的介绍
java·开发语言·spring
caihuayuan42 小时前
React Native 0.68 安装react-native-picker报错:找不到compile
java·大数据·sql·spring·课程设计
(・Д・)ノ2 小时前
python打卡day29
开发语言·python
若水晴空初如梦2 小时前
QT聊天项目DAY11
开发语言·qt
爱编程的鱼2 小时前
C#接口(Interface)全方位讲解:定义、特性、应用与实践
java·前端·c#
有杨既安然2 小时前
Python高级特性深度解析:从熟练到精通的跃迁之路
开发语言·python·数据挖掘·flask