再谈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.如果没有则入池。

相关推荐
探索java11 分钟前
Spring MVC框架中全局异常处理机制详解
java·spring·mvc
意疏25 分钟前
浙江大学PTA程序设计C语言基础编程练习题6-10
c语言·开发语言
AI必将改变世界30 分钟前
【软考系统架构设计师备考笔记5】 - 专业英语
java·开发语言·人工智能·笔记·系统架构·英语
_祝你今天愉快34 分钟前
Java Lock
android·java·后端
listhi52034 分钟前
Python实现信号小波分解与重构
开发语言·python·重构
熊猫片沃子1 小时前
mybatis 与mybatisplus 比较总结
java·后端·mybatis
骑驴看星星a1 小时前
层次分析法代码笔记
开发语言·笔记·python·numpy
Elastic 中国社区官方博客2 小时前
升级 Elasticsearch 到新的 AWS Java SDK
java·大数据·elasticsearch·搜索引擎·云计算·全文检索·aws
枣伊吕波2 小时前
十一、请求响应-请求:简单参数和实体参数(简单实体参数与复杂实体参数)
java·spring boot·后端
苇柠2 小时前
SpringMVC基础
java·后端·spring