String常量池
Java常量池划分:
- 字面量:给基本类型变量赋值的方式就叫做字面量或者字面值。 比如:String a=“b” ,这里“b”就是字符串字面量,同样类推还有整数字面值、浮点类型字面量、字符字面量。
用final修饰的成员变量、静态变量、实例变量、局部变量 - 符号引用:类和接口的全限定名(Full Qualified Name),也就是Ljava/lang/String;,主要用于在运行时解析得到类的直接引用
字段的名称和描述符(Descriptor),字段也就是类或者接口中声明的变量,包括类级别变量(static)和实例级的变量
方法的名称和描述符 - 字符串常量池:字符串常量池里的内容是在类加载、验证、准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中。
在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表。
这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。
new String("hello")做了什么:
在字符串常量池中创建一个字符串hello。
new关键字创建一个实例对象String,并指向字符串常量池hello的引用。
注意:
String s1 = new String("he") + new String("llo");
s1涉及动态拼接,底层调用StringBuilder.append("he").append("llo").toString();,StringBuilder拼接后的toString()方法不会将字符串存入常量池,只会在堆中创建新对象
这种情况也不会放入常量池:
String s6 = new String(new char[]{'h', 'e', 'l', 'l', 'o'}); String s7 = s6.intern();
在构建String s6的时候,使用new String(new char[]{'h', 'e', 'l', 'l', 'o'})初始化字符串时(不会自动调用intern(),字符串采用懒加载方式进入到常量池),并没有在字符串常量池中构建hello这个字符串实例。
所以当调用s6.intern()方法时,会把该String对象添加到字符常量池中,并返回对该String对象的引用,所以s6和b指向的引用地址是同一个。
intern()方法做了什么:
intern()方法的作用是:拿String的内容去Stringtable里查表,如果存在,则返回引用,不存在,就把该对象的"引用"保存在Stringtable表里,注意并不会新建对象。
注意:JDK1.6及以前,字符串常量池在永久代里,StringTable不存在时,会在永久代上新建一个实例,并存入常量池中;
JDK1.7及以后,字符串常量池挪到了堆里,因此,JDK 7(以及部分其他虚拟机,例如JRockit)的intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,即不存在不会复制,直接返回堆中对象的地址,堆中对象的地址和常量池中的地址是一样的。
下面这段代码:
String s1 = new String("he") + new String("llo"); //涉及动态拼接,底层调用 //StringBuilder.append("he").append("llo").toString(); //StringBuilder拼接后的toString()方法不会将字符串存入常量池,所以上述JDK1.7为true String s2 = s1.intern();//s1.intern()返回的仍是s1的引用地址 System.out.println(s1 == s2);// jdk1.7及以后true,jDK1.6及以前false
jdk6:
jdk7:
例子:
public static void main(String[] args) {
String s1 = new String("he") + new String("llo");// 涉及动态拼接,底层调用StringBuilder.append("he").append("llo").toString();
// StringBuilder拼接后的toString()方法不会将字符串存入常量池,所以上述JDK1.7为true,s1.intern()返回的仍是s1的引用地址
String s2 = s1.intern();
System.out.println(s1 == s2); // jdk1.7及以后true,jDK1.6及以前false
System.out.println("=============================");
String s3 = "hello";
String s4 = new String("he") + new String("llo"); // 涉及动态拼接,底层调用StringBuilder.append("he").append("llo").toString();
String s5 = s1.intern();
System.out.println(s4 == s5); // jdk1.7及以后false,jDK1.6及以前false
System.out.println("=============================");
String s6 = new String(new char[]{'h', 'e', 'l', 'l', 'o'});
String s7 = s6.intern();
System.out.println(s6 == s7);// true
System.out.println("=============================");
String s8 = new String("hello");
String s9 = s8.intern();
System.out.println(s8 == s9);// false
}