Java—String类的intern方法的学习

对intern方法的学习来源于之前偶然看到的一篇博客:【请别再拿“String s = new String("xyz");创建了多少个String实例”来面试了吧】

博客地址:https://www.iteye.com/topic/774673

这篇博客写的很好,从各方面详细的讲解了Java底层的一些东西,不搞底层的看着可能会觉得很涩,很难懂,有兴趣的可以看看,当然本篇重点不在这边,而是对intern方法的使用方面的解析

在JDK1.6版本及以前,字符串常量池是存放在永久代中的,当使用intern方法的时候,会先查询字符串常量池中是否存在当前的字符串,如果不存在的话,就会将当前的字符串复制到字符串常量池中,并返回字符串常量池的引用。

JDK1.7以后,字符串常量池是存放在堆中的,当使用intern方法的时候,也会先查询字符串常量池中是否存在当前的字符串,如果不存在,再从堆中查询,然后存储并返回相关引用。如果都不存在的话,就会将当前的字符串复制到字符串常量池中,并返回字符串常量池的引用。

借用美团技术团队《深入解析String#intern》一文中两段代码与相关图片来进行解释:

第一段代码:

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);

JDK1.6版本的运行结果为false false

解析:

        String s = new String("1"),在字符串常量池中创建了“1”对象,在堆中创建了s对象。

        s.intern(),因为字符串常量池已经有了“1”对象,所以可以跳过。

        String s2 = "1",由于字符串常量池中已经存在“1”对象,所以s2直接指向字符串常量池中的“1”对象。

        所以,s是堆中对象的引用,而s2指向的是字符串常量池,结果为false。 

        String s3 = new String("1") + new String("1"),在堆中创建了“1”对象,在字符串常量池中创建了s3对象。

        s3.intern(),是直接将“11”复制到字符串常量池中。

        String s4 = "11",指向了字符串常量池中的“11”。

        所以,s3是堆中的引用,而s4指向的是字符串常量池,结果为false。

JDK1.7版本的结果为false true 

解析:

        s和s2的结果为false的原因同上。

        而s3.intern(),返回的是堆中的引用,所以s4指向的其实也是堆中的引用,所以结果为true。

第二段代码:

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);

这里将s2、s4对象的创建和intern方法的使用交换了一下顺序。

JDK1.6版本的运行结果为false false

解析:

        上半段代码先在堆中创建了s对象,在字符串常量池创建了“1”对象,s2指向字符串常量池的“1”,这里的intern也就没有了实际的意义。所以一个指向堆中的引用,一个指向字符串常量池,false。

        下半段代码是先在堆中创建了s3对象,在字符串常量池创建了“1”对象,又在字符串常量池中创建了“11”对象,所以,s3指向堆中的引用,s4指向字符串常量池,false。

JDK1.7版本的运行结果同样为false false

        原理和1.6版本的解析基本一致,都是false,不是true,是因为上面的为true的是intern先执行,会存储堆中的引用,而这里是先执行的是String s4 = "11",在查询之前就重新创建了一个“11”对象,所以s3和s4指向的不是同一个,执行结果为false。

第三段代码:

static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];

public static void main(String[] args) throws Exception {
    Integer[] DB_DATA = new Integer[10];
    Random random = new Random(10 * 10000);
    for (int i = 0; i < DB_DATA.length; i++) {
        DB_DATA[i] = random.nextInt();
    }
	long t = System.currentTimeMillis();
    for (int i = 0; i < MAX; i++) {
        arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
        //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
    }

	System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}

使用intern的结果为2044ms,不适用intern的结果为5016ms

总结一下:intern方法返回的是字符串对象的规范化表示形式,初衷就是为了让string对象能够被重复的使用,以此来节省内存的损耗。更深层次的对intern的解析可以参照:

美团技术团队《深入解析String#intern》 https://tech.meituan.com/in_depth_understanding_string_intern.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值