String Pool
String Pool,字符串常量池。(位于堆中)
常量池在java用于保存在编译期已确定的,已编译的class文件中的一些数据。
它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;
当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,常量池存在于方法区中。
每一个都是常量池中的一个常量表(常量项)。而这些常量表之间又有不同,class文件共有11种常量表,如下所示:

2.1采用字面值赋值方式创建对象
String str1 = "aaa";
String str2 = "aaa";当以字符串直接创建String类的对象时,会在字符串池中查找是否已经存在该常量。
如上例中,会在String Pool中查找是否存在"aaa"这个对象,如果不存在,则在String Pool中创建一个"aaa"对象,然后将其地址返回,赋给变量str1,这样str1会指向String Pool中的这个"aaa"对象。
如果在String Pool中查找时已经存在该对象,则不创建任何对象,直接将String Pool中的这个对象地址返回,如上面str2直接得到"aaa"对象的地址,它和str1实际上指向同一个对象。
2.2采用new的形式创建对象
采用new的形式创建对象:
String str = new String("aaa");实际上创建了两个String对象,一个是”aaa”对象,存储在常量空间中,一个是使用new关键字为对象申请的堆空间。
这种形式创建字符串对象时仍然会在String Pool中查找。
如果没有,则首先在String Pool中创建相应的对象,然后再在堆中创建一个对象;
若相同对象已经存在,则在堆中创建对象。
无论哪种情况,最后返回的地址都是堆中的地址。
如上例,首先在String Pool中查找有没有"aaa"这个字符串对象,如果有,则不在String Pool中再去创建"aaa"这个对象了,直接在堆(heap)中创建一个"aaa"字符串对象,然后将堆中的这个"aaa"对象的地址返回来,赋给str引用。
如果没有,则首先在String Pool中创建一个"aaa"对象,然后再在堆中创建一个"aaa"对象,然后将堆中的这个"aaa"对象的地址返回来,赋给str引用。
2.3关于String创建对象的数量
(1) 、 String a = "abc";
这样创建 1 个对象。
JVM先到字符串池中查找,看是否已经存在值为"abc"的对象,如果存在,则不再创建新的对象,直接返回已存在对象的引用;
如果不存在,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
(2)、String a = new String("abc");
这样创建 2 个对象。
一个 是放在常量池中的”abc“对象。
另一个是由 new String()构造器在堆中创建一个“abc”对象。
a本身只是一个引用,放在栈里,用来指向堆中创建出来的对象。
(3) String a = "abc";
String b = "abc";
这样也只创建 1 个对象。
只在常量池中创建一个对象 "abc",然后将引用分别返回给a 和b。
(4) String a = "abc" + "def";
这样创建 1 个对象。
由于常量字符串在编译时候也就确定的,又因为“abc”和“def”都是字符串常量,因此变量 a 的值在编译时就可以确定了。与 String a = "abcdef"; 一样。
(5) :
publicclass Concatenation2 {
publicstaticvoid main(String[] args) {
String a = "abc";
String b = "def";
String c = "abcdef";
String d = "abc" + "def"; //不会产生新的字符串
System.out.println(c == d); //true
String e = a + "def";//会产生新的字符串对象
System.out.println(e == c); //false
String f = a + b; //会产生新的字符串
System.out.println(f == c); //false
}
}结论:如果“+”连接的两字符串中只要有一个不是字面常量串(即定义过的),是会产生新的字符串对象。
(6)、
如果将 a 和 b 都定义为常量,则:
publicclass Concatenation2 {
publicstaticvoid main(String[] args) {
final String a = "abc";
final String b = "def";
String c = "abcdef";
/*
* 因为 a 和 b 都被定义成了常量,所以编译时就确定。
* 编译时会将常量替换,等同于 String d = "abc" + "def";
*/
String d = a + b;
System.out.println(d == c); //true
}
}
如果将 a 和 b 只有一个定义为常量,则:
publicclass Concatenation2 {
publicstaticvoid main(String[] args) {
finalString a = "abc";
String b = "def";
String c = "abcdef";
/*
* 因为 a 为常量编译时就确定,而b为不是常量不能编译时确定
* 因此等同于 String d = "abc" + b"
*/
String d = a + b;
System.out.println(d == c); //false
}
} (7)、如果 a 和 b 都定义为 final ,但不在定义时候初始化,而是在static 块中初始化:
publicclass Concatenation2 {
finalstatic String a;
finalstatic String b;
//此时 a 和 b 类似于变量,在类首次加载时才进行初始化
static{
a = "abc";
b = "def";
}
publicstaticvoid main(String[] args) {
String c = "abcdef";
String d = a + b;
System.out.println(c == d); //false
}
}
















