非常神奇的Interned Strings

在Java语言中有8种基本类型和一种比较特殊的类型String。这些类型为了使它们在运行过程中速度更快、更节省内存,都提供了一种常量池的概念。常量池就类似一个Java系统级别提供的缓存。8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:
(1)直接使用双引号声明出来的String对象会直接存储在常量池中。
(2)如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
通俗点讲,Interned String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(String Intern Pool)。

一个栗子如下

public class Main {
    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

在JDK6中常量池是放在Perm区中,和正常的Java Heap区域完全分开。哪一个Java Heap区域的对象地址和字符串常量的对象地址比较,肯定是不相同的,即使调用String.intern方法也没有任何关系。

JDK7和JDK8——运行结果为false,true

在JDK7版本中,字符串常量池已经从Perm区移到正常的Java Heap区域了。String s3=new String(“1”)+new String(“1”)这句代码中生成了两个对象,是字符串常量池中的“1”和Java Heap中的s3指向的对象。接下来s3.intern()将s3中的”11“字符串放入常量池中,由于常量池在Java Heap区,因此常量池中不需要再存储一份对象了,可以直接存储堆中的引用,这个引用指向s3引用的对象,也就是说引用地址是相同的。最后s4是显式声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向s3引用对象的一个引用。
如果s4先声明就会是false啦

我的白话总结:)

通过new得到的String,都会在堆里生成一个对象,使用了intern之后,会从字符串常量池中查询当前字符串是否存在,若不存在就中会将当前字符串放入常量池中。
这个“放入”就非常有意思了:
在JDK6中,由于常量池和堆分开,所以放入就,是再copy一份到常量池,那么就算后面显式声明一个内容相同的字符串,它的地址是在常量池,==永远不会是true;
在JDK7和JDK8中,由于常量池和堆在一起,所以放入就是,常量池中放入堆中该对象的地址,如果后面显示声明一个内容相同的字符串,==就会是true了。但是如果在intern之前,常量池中已经有了当前字符串,那么就不会相等了。s1==s2是false就是因为new的时候,现在常量池中放了一个”1“,那么s1就会在堆里,s2还是在常量池咯!
感觉没什么用但是蜜汁有趣系列emmm

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值