一:常量池:
在java编译好的class文件中,有个区域称为Constant Pool,他是一个由数组组成的表,类型
为cp_info constant_pool[],用来存储程序中使用的各种常量,包括Class/String/Integer等各种基本Java数据类型
虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用常量的一个有序集合,包括直接常量(String ,integer和floating point常量)和对其他类型、字段和方法的符号引用。池中的数据项就像数组一样是通过索引访问的。因为常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在Java程序的动态连接中起着核心作用
常量池包含了与文件中类和接口相关的常量。常量池中存储了诸如文字字符串、final变量值、类名和方法名的常量。Java虚拟机把常量池组织为入口列表的形式。在实际列表constant_pool之前,是入口列表中的技术constant_pool_count!
每个常量池入口都从一个长度为一个字节的标志开始,这个标志指出了列表中该位置的常量类型。一旦java虚拟机获取并解析这个标志,Java虚拟机就会知道在标志后的常量类型是什么!
在动态链接的Java程序中,常量池充当了十分重要的角色。除了字面常量(或者说直接量)值以外,常量池还可以容纳下面几种符号引用。
● fully qualified names of classes and interfaces 类和字段的全限定名。
● field names and descriptors 字段的名称和描述符
● method names and descriptors 方法的名称和描述符
对于Constant Pool,表的基本通用结构为:
cp_info {
u1 tag;
u1 info[];
}
常量池是一个可变长度cp_info表的有序序列。Cp_info表中的tag(标志)项是一个无符号的byte类型值,它表明了表的类型和格式cp_info表一共有11中类型。
tag是一个数字,用来表示存储的常量的类型,例如8表示String类型,5表示Long类型,info[]根据
类型码tag的不同会发生相应变化.
二:
String Pool是对应于在Constant Pool中存储String常量的区域!
三:
public class Test {
public static void main(String[] args) {
String s1 = “sss111”;
String s2 = “sss111”;
}
}
注:
1–对于n个值相同的String常量,在Constant Pool中只会创建一个
2–执行的时候,Constant Pool会储存在Method Area,而不是heap中
3–对于”“内容为空的字符串常量,会创建一个长度为0,内容为空的字符串放到Constant Pool中,
而且Constant Pool在运行期是可以动态扩展的.
四:String
1 –String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能
再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的(immutable)
<可以通过反射方式修改里面的值,不可变的是引用>
2 –String类有一个特殊的创建方法,就是使用”“双引号来创建.例如new String(“i am”)实际创建了2个String对象,一个是”i am”通过”“双引号创建的,另一个是通过new创建的.只不过他们创建的时期不同
一个是编译期,一个是运行期!
3 –java对String类型重载了+操作符,可以直接使用+对两个字符串进行连接
4 –运行期调用String类的intern()方法可以向String Pool中动态添加对象
String的创建方法一般有如下几种
1.直接使用”“引号创建
2.使用new String()创建
3.使用new String(“someString”)创建以及其他的一些重载构造函数创建
4.使用重载的字符串连接操作符+创建
String s1 = "hello";
String s2 = "hello";
由于String Pool只会维护一个值相同的String对象
上面2句得到的引用是String Pool中同一个对象,所以
他们引用相等
System.out.println(s1 == s2);//true
String s1 = new String("hello");
String s2 = "hello";
由于s1是new出的新对象,存储在heap中,s2指向的对象
存储在String Pool中,他们肯定不是同一个对象,只是
存储的字符串值相同,所以返回false.
System.out.println(s1 == s2);//false
String s1 = new String("hello");
String s2 = "hello";
s1=s1.intern();
当调用intern方法时,如果String Pool中已经包含一个等于此String对象
的字符串(用 equals(Object)方法确定),则返回池中的字符串.否则,将此
String对象添加到池中,并返回此String对象在String Pool中的引用.
System.out.println(s1 == s2);
String s1 = new String(“111”);
String s2 = “sss111”;
/*
* 由于进行连接的2个字符串都是常量,编译期就能确定连接后的值了,
*
* 编译器会进行优化直接把他们表示成”sss111”存储到String Pool中,
*
* 由于上边的s2=”sss111”已经在String Pool中加入了”sss111”,
*
* 此句会把s3指向和s2相同的对象,所以他们引用相同.此时仍然会创建出
*
* “sss”和”111”两个常量,存储到String Pool中.
*/
String s3 = “sss” + “111”;
/*
*
* 由于s1是个变量,在编译期不能确定它的值是多少,所以
*
* 会在执行的时候创建一个新的String对象存储到heap中,
*
* 然后赋值给s4.
*/
String s4 = “sss” + s1;
System.out.println(s2 == s3); // true
System.out.println(s2 == s4); // false
System.out.println(s2 == s4.intern()); // true
结果上面分析,总结如下:
1.单独使用”“引号创建的字符串都是常量,编译期就已经确定存储到String Pool中.
2.使用new String(“”)创建的对象会存储到heap中,是运行期新创建的.
3.使用只包含常量的字符串连接符如”aa” + “aa”创建的也是常量,编译期就能确定,已经确定存储到String Pool中.
4.使用包含变量的字符串连接符如”aa” + s1创建的对象是运行期才创建的,存储在heap中.
6.使用”aa” + s1以及new String(“aa” + s1)形式创建的对象必须调用intern()方法才会加入String Pool!