Constant Pool常量池的概念:
在讲到String的一些特殊情况时总会提到String Pool或者Constant Pool但是我想很多人都不太明白Constant Pool到底是个怎么样的东西运行的时候存储在哪里所以在这里先说一下Constant Pool的内容
String Pool是对应于在Constant Pool中存储String常量的区域习惯称为String Pool也有人称为String Constant Pool好像没有正式的命名
在java编译好的class文件中有个区域称为Constant Pool他是一个由数组组成的表类型为cp_info constant_pool[]用来存储程序中使用的各种常量包括Class/String/Integer等各种基本Java数据类型详情参见The Java Virtual Machine Specification 章节
对于Constant Pool表的基本通用结构为:
cp_info { u tag; u info[];}
tag是一个数字用来表示存储的常量的类型例如表示String类型表示Long类型info[]根据
类型码tag的不同会发生相应变化
对于String类型表的结构为:
CONSTANT_String_info { u tag; u string_index;}
tag固定为string_index是字符串内容信息类型为:
CONSTANT_Utf_info { u tag; u length; u bytes[length];}
tag固定为length为字符串的长度bytes[length]为字符串的内容
(以下代码在jdk中编译)
为了详细理解Constant Pool的结构我们参看一些代码:
String s = sss; String s = sss; Systemoutprintln(s + + s);
由于sss和sss都是字符串常量在编译期就已经创建好了存储在class文件中
在编译后的class文件中会存在这个常量的对应表示:
; sss ; sss
根据上面说的String常量结构我们分析一下
开始的为CONSTANT_String_info结构中的tag而应该是它的相对引用为CONSTANT_Utf_info的tag为对应字符串的长度 为字符串对应的编码接着分析会发现后面的是对应sss的存储结构
经过上面分析我们知道了和是两个字符串的相对引用就可以修改class文件来修改打印的内容把class文件中的 E C D改成 E C D程序就会输出sss sss而不是和原程序一样输出sss sss因为我们把对sss的相对引用改成了对sss的相对引用
public class Test { public static void main(String[] args) { String s = sss; String s = sss; }}
在上面程序中存在个相同的常量sss对于n个值相同的String常量在Constant Pool中只会创建一个所以在编译好的class文件中我们只能找到一个对sss的表示:
abh: ; sss
在程序执行的时候Constant Pool会储存在Method Area而不是heap中
另外对于内容为空的字符串常量会创建一个长度为内容为空的字符串放到Constant Pool中
而且Constant Pool在运行期是可以动态扩展的
关于String类的说明
String使用private final char value[]来实现字符串的存储也就是说String对象创建之后就不能再修改此对象中存储的字符串内容就是因为如此才说String类型是不可变的(immutable)
String类有一个特殊的创建方法就是使用双引号来创建例如new String(i am)实际创建了个
String对象一个是i am通过双引号创建的另一个是通过new创建的只不过他们创建的时期不同
一个是编译期一个是运行期!
java对String类型重载了+操作符可以直接使用+对两个字符串进行连接
运行期调用String类的intern()方法可以向String Pool中动态添加对象
String的创建方法一般有如下几种
直接使用引号创建
使用new String()创建
使用new String(someString)创建以及其他的一些重载构造函数创建
使用重载的字符串连接操作符+创建
例
String s = sss; //此语句同上 String s = sss; Systemoutprintln(s == s); //结果为true
例
String s = new String(sss); String s = sss; Systemoutprintln(s == s); //结果为false
String s = new String(sss); s = sintern(); String s = sss; Systemoutprintln(s == s);
例
String s = new String(); String s = sss; String s = sss + ; String s = sss + s; Systemoutprintln(s == s); //true Systemoutprintln(s == s); //false Systemoutprintln(s == sintern()); //true
例
这个是The Java Language Specification中节的例子有了上面的说明这个应该不难理解了
package testPackage; class Test { public static void main(String[] args) { String hello = Hello lo = lo; Systemoutprint((hello == Hello) + ); Systemoutprint((Otherhello == hello) + ); Systemoutprint((otherOtherhello == hello) + ); Systemoutprint((hello == (Hel+lo)) + ); Systemoutprint((hello == (Hel+lo)) + ); Systemoutprintln(hello == (Hel+lo)intern()); } } class Other { static String hello = Hello; } package other; public class Other { static String hello = Hello; }
输出结果为true true true true false true请自行分析!
结果上面分析总结如下:
单独使用引号创建的字符串都是常量编译期就已经确定存储到String Pool中
使用new String()创建的对象会存储到heap中是运行期新创建的
使用只包含常量的字符串连接符如aa + aa创建的也是常量编译期就能确定已经确定存储到String Pool中
使用包含变量的字符串连接符如aa + s创建的对象是运行期才创建的存储在heap中
使用aa + s以及new String(aa + s)形式创建的对象是否加入到String Pool中我不太确定可能是必须调用intern()方法才会加入希望高手能回答!
还有几个经常考的面试题:
String s = new String(s) ;String s = new String(s) ;上面创建了几个String对象?答案:个 编译期Constant Pool中创建个运行期heap中创建个
String s = s;String s = s;s = s;s指向的对象中的字符串是什么?答案: s