Java运行时常量池和字符串常量池使用

存储内容

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 Intern 方法详解
        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的作用

intern 的作用是把 new 出来的字符串的引用添加到 stringtable java 会先计算 string hashcode ,查找stringtable 中是否已经有 string 对应的引用了,如果有返回引用(地址),然后没有把字符串的地址放到stringtable 中,并返回字符串的引用(地址)。
 
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虚拟机》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值