存储内容
clss文件中除了有类的版本方法 , 字段 , 接口等描述信息外 , 还有一项信息是常量池 , 用于存放编译期生成的各种字面量喝符号引用 , 这部分内容在类加载后进入方法区的运行时常量池中存放。
字面量 :
- 双引号引起的字符串值 , "abc"
- 定义为final类型的常量的值
符号引用:
- 类或者接口的全限定名(包括父类和所实现的接口)
- 变量或者方法的名称
- 变量或方法的描述信息
- 方法的描述 : 参数个数 , 参数类型 , 方法返回类型等
- 变量的描述信息 : 变量的返回值
- this
运行时常量池相对于class文件常量池的另一个重要特征是具备动态性 , java语言并不要求常量一定只有编译期才产生的 , 也就是并非预置入class文件中常量的内容才能进入方法区运行时常量池 , 运行期间也可能将新常量放入池中 , 这种特性开发人员使用较多的是String类的intern()方法。
存储位置
在JDk1.6及以前,运行时常量池的 方法区 一部分。
在JDk1.7及以后,运行时常量池在 java堆 中。
运行时与class常量池一样 , 运行时常量池也是每个类都有的一个, 但是字符串常量池只有一个局。
字符串常量池如何存储数据
为了提高匹配的速度 , 即更快的查找某字符串是否存在于常量池 , java在设计字符串常量池的时候 , 还搞一张stringtable , stringtable有点类似于hashtable , 保存字符串的引用。
- 在JDK6中stringtable是固定的 , 1009长度 , 因此若放入string Pool中String 非常多 , 就会造成hash的冲突 , 导致链表过长 , 但调用String.intern()时需要到链表上一个一个找 , 从而导致性能降低 。
- 在jdk7中stringtabkle的长度可通过一个参数指定 : -XX:StringTableSize=99999
字符串常量池查找字符串的方式:
- 根据字符串发hashcode找到对应entry , 若没冲突 , 它可能是一个entry , 若冲突 , 它可能一个entry链表 , 然后Java再遍历entry链表 , 匹配引用对应的字符串 。
- 若找到字符串 , 返回引用 。若找不到字符串 , 会把字符串放到常量池 , 并把引用保存到stringtable里 。
字符串常量池案例分析
public class Test {
public void test() {
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1 == str2);
String str3 = new String("abc");
System.out.println(str3 == str2);
String str4 = "a" + "b";
System.out.println(str4 == "ab");
final String s = "a";
String str5 = s + "b";
System.out.println(str5 == "ab");
String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
System.out.println(str6 == "ab");
String str7 = "abc".substring(0, 2);
System.out.println(str7 == "ab");
String str8 = "abc".toUpperCase();
System.out.println(str8 == "ABC");
String s3 = "ab";
String s4 = "ab" + getString();
System.out.println(s3 == s4);
String s5 = "a";
String s6 = "abc";
String s7 = s5 + "bc";
System.out.println(s6 == s7.intern());
}
private String getString(){
return "c";
}
}
String a = "hello";
String b = new String("hello");
System.out.println(a == b);
String c = "world";
System.out.println(c.intern() == c);
String d = new String("mike");
System.out.println(d.intern() == d);
String e = new String("jo") + new String("hn");
System.out.println(e.intern() == e);
String f = new String("ja") + new String("va");
System.out.println(f.intern() == f);
若此题可以一题不差做对 , 接下来的内容可不用看 , 若不能并有兴趣的 , 可了解一下内容 。
intern的作用
public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
}
打印结果 : jdk6以下 false false jdk7上 false true 然后将 s3.intern(); 语句下调一行,放到 String s4 = "11"; 后面。将s.intern(); 放到 String s2 = "1"; 后面。是什么结果呢?
public static void main(String[] args) {
String s = new String("1");
String s2 = "1";
s.intern();
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);
}
打印结果为 : jdk6 下 false false jdk7上 false false
参考资料:《深入理解Java虚拟机》