为什么要使用String.intern()

 最近在阅读《深入理解Jav虚拟机》的运行时常量池章节,看到“java语言并不要求常量池一定只有编译器才能产生...运行期间也可以将新的常量放入常量池,这种特性被开发人员利用得比较多的时String类的intern()方法。"于是我便去深入了解了一下。

1     public static void main(String[] args) {
2         String a="王者";
3         String b="荣耀";
4         String c=a+b;             //编译期常量池不存在"王者荣耀"
5         String d=c.intern();
6         System.out.println(d==c);
7     }         

上面为什么是true呢,接下来讲述intern()

JDK1.7后,intern方法先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,当字符串常量池中找不到对应的字符串时,而只是生成一个对该字符串的引用在字符串常量池。所以”王者荣耀”本身不会在字符串常量池

前面说了intern可以在程序运行时将新的常量放入运行时常量池,接下来就开始演示intern的强大用处

 public static void main(String[]  args) throws Exception {
        long start=System.currentTimeMillis();   //获取开始时间
        String[] arr = new String[10000000];
        Integer[] a= new Integer[10000000];
        for (int i = 0; i < 10000000; i++) {
            a[i] = (int)(1+Math.random()*(3));
        }
        for (int i = 0; i < 10000000; i++) {
            arr[i] = new String(String.valueOf(a[i]));
        }
        long end=System.currentTimeMillis(); //获取结束时间
        System.out.println("程序运行时间: "+(end-start)+"ms");
    }
    //程序运行时间: 14461ms
    public static void main(String[]  args) throws Exception {
        long start=System.currentTimeMillis();   //获取开始时间
        String[] arr = new String[10000000];
        Integer[] a= new Integer[10000000];
        for (int i = 0; i < 10000000; i++) {
            a[i] = (int)(1+Math.random()*(3));
        }
        for (int i = 0; i < 10000000; i++) {
            arr[i] = new String(String.valueOf(a[i])).intern();
        }
        long end=System.currentTimeMillis(); //获取结束时间
        System.out.println("程序运行时间: "+(end-start)+"ms");
    }
    //程序运行时间: 1688ms

就是有无调用intern方法,但程序运行时间差了10倍!

我们明确的知道,会有很多重复的相同的字符串产生,但是这些字符串的值都是只有在运行期才能确定的。所以,我们通过intern显示的将其加入运行时常量池

因为随机数是运行期才知道的,假设整个过程只产生”1“,“2”,”3“的字符串,调用了intern()就会把的堆内对象索引放入了运行常量池,下次在看见”1“,“2”,”3“,返回第一次在堆中String的对象地址,不用创建新对象,使用intern()在堆中只会创建6个对象(堆+方法区),而不使用intern则创建100000000个对象,并且常量池里并没有”1“,“2”,”3“

当在编译期知道确定了字面量,new String("王者荣耀").intern()没什么意义的,直接就返回了运行时字符串的地址,但intern()并不是这样的初衷,而是为了解决运行时才出现的常量,不是解决编译时在字符串常量池的问题

 

public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        String str1 = new StringBuilder("计算机").append("软件").toString();
        System.out.println(str1.intern() == str1);
        //true

        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern() == str2);
        //flase
    }
}

最好测试字符串采取选用中文,书中有所描述,防止类加载时,默认加载类加载了字符串导致常量池中以存在真正的字面量

技术有限,如有错误,请指正,谢谢

posted @ 2022-02-25 13:40  今晚螃蟹下酒  阅读(212)  评论(0)    收藏  举报