String与字符串池

76 阅读3分钟

1.字符串池的作用与实现

作用:字符串是一个常用的类,为了减少相同的字符串的重复创建而占用过多内存,保证相同的字符串常量必须指向同一个String实例,因而有了字符串池。

实现:不同虚拟机以及不同的版本中对字符串池的实现方式都不太一样。HotSpot中定义了StringTable来保存字符串引用。在JDK1.6以前字符串池位于永久代,因为可能导致内存泄漏,所以在JDK1.7开始字符串池放到了堆内存。

2.字符串池中内容的来源

运行时字符串常量池中包含多种不同的常量,内容来源主要是编译期可知的字面量和符号引用以及运行期解析后获得的常量

2.1 编译期可知的字面量与符号引用其实就是Class文件的常量。Class文件除了包含类的版本号、字段、方法、接口等描述信息,还有一项信息就是常量池,用于存放编译器生成的各种字面量(由数字字母等构成的字符串)和符号引用(类和接口的全限定名、字段的名称和描述符、方法的名称和描述符),所有在程序中被""定义的字符串都会作为常量放到Class常量池中Class常量池中的字面量会在该字符串第一次被调用的时候加入到运行时字符串池中,创建成内容为该字面量的String对象

2.2 运行期解析后获得的常量指用intern方法引入的字符串。字符串对象调用intern方法,如果字符串常量池中存在当前字符串,则直接返回常量池中它的引用;如果常量池中没有此字符串,则会将此字符串的引用放入常量池,然后再返回这个引用。

比如:


String s1 = new String("Hello") + "World";
System.out.println(s1.intern() == s1);//true

String s2 = new String("Hell") + new String("oWorld");
System.out.println(s2.intern() == s2);//false

在编译过后,Class常量池中会有Hello、World、Hell、oWorld这几个字符串,在第一次被调用时会在字符串池中创建对象。

new String("Hello") + "World";用+拼接相当于在堆中创建字符串对象并用StringBuider将两个字符串拼接起来,然后将引用赋给s1,s1调用intern()方法,此时字符串池中没有与HelloWorld能equals为true的字符串,则将s1的引用放到字符串池中,所以s1.intern() == s1为true。

new String("Hell") + new String("oWorld");在堆中创建了三个对象,内容分别是Hell、oWorld以及HelloWorld,并将内容为HelloWorld的对象引用赋给s2,s2调用intern()方法,此时字符串池中存在与s2指向对象equals为true的对象,所以s2.intern()返回的是s1的引用,故而结果为false。

3.一个问题

new String("abc")会创建几个对象?

"abc"作为字面量,会在第一次被调用的时候在字符串池中创建成String对象,所以问题在于该语句之前是否有"abc"这个字面量的调用,如果有则只在堆中创建对象,如果没有则在字符串池和堆中同时创建对象。