目录
一.问题的引入
对于下面例子中,我们可以知道它们的结果是怎样的
字符串是不可变的,可能我们会想这几个字符串的内容都一样会不会都是相等的。
在这里我们可能会想到对于new的对象,它们的地址肯定是不相同的,但是对于str1和str2也是字符串类型的对象。
我们再通过debug来看它们的地址有什么区别。
要想了解这几个字符串在Java中是如何进行引用的,就需要知道常量池以及字符串常量池。
二.字符串常量池
1.引入字符串常量池的原因
对于重复的字符串来说,Java为了使运行速度更快,更节省内存,所以通过字符串常量池来进行管理字符串。
例子:比如吃瓜子的瓜子皮扔进垃圾桶,方法1:每吃一个,去垃圾桶扔一次;方法2:将瓜子皮放到手中,当手中满了,再去垃圾桶扔。这里的方法2的效率就很高,相当于池化技术。
2.需要了解的三种常量池
(1) Class文件常量池:每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息.
(2)运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份.
(3)字符串常量池.
常量池中的内容管理相当于使数组一样,需要使用时就像访问数组中的索引一样。
3.详解关于字符串对象问题
关于问题的引入中str1,str2,str3,str4在字节码中常量池详情
(1)在字节码中查看
关于其中
(2)问题
(3)问题图解
在字节码文件加载时“aaaa”常量串已经创建好并加载到了字符串常量池中。
从下图结果我们可以得到关于字符串中内容是如何创建的
这里str1和str2存放的是“aaaa”对象的引用,而str3和str3中是创建一个新的对象,从而将原本"aaaa"对象中的hash和value拷贝一份给当前对象。
这里面设计到了数据结构中的哈希表管理数据,对于哈希表中的每一个内容,又通过单链表来进行管理,单链表中每一个结点中有该结点的哈希码,string内容(内容地址),和下一个结点地址,如果没有就为null.
(4)关于字符串常量池的注意事项
1. 在JVM中字符串常量池只有一份,是全局共享的。当多个类同时运行时,使用的是同一份字符串常量池。
2. 刚开始字符串常量池是空的,随着程序不断运行,字符串常量池中元素会越来越多。
3. 当类加载时,字节码文件中的常量池也被加载到JVM中,称为运行时常量池,同时会将其中的字符串常量保存在字符串常量池中。
4. 字符串常量池中的内容:一部分来自运行时常量池,一部分来自程序动态添加。
(5)总结
1.通过上面图解可知,只要是new的对象,都是唯一存在的。
2.可以发现关于实例化对象的时候,直接实例化,只会开辟一块内存空间,而使用new来实例化使用两块内存空间,所以直接实例化会节省内存空间。
3.我们知道对于实例化时还有一种是使用字符数组,但是字符数组不是直接加入到常量池中,我们可以进行验证
这里是因为ch字符数组会在堆上创建,new字符串对象的时候,会将堆上ch的内容拷贝到str5对象中的value中,和字符串常量中的对应的value不是同一份,所以结果会不想等。
4.intern()方法的使用
该方法的作用是手动将创建的String对象添加到常量池中。且该方法是一个本地方法。
对于使用字符数组创建字符串对象,我们可以使用该方法手动将该对象添加到字符串常量池中。
具体使用: