关于Java String类的intern方法与字符串常量池分析

目录
1. String类简述
2. 字符串常量池
3. intern方法与字符串比较

String类简述

String作为Java中一个较为特殊的类,无论在开发过程还是面试时都是一个值得注意的地方。
首先,String类是一个final类,这意味着它不可被继承同时一旦创建了一个String对象不能被修改。关于这点,通过下面这段代码来解释:

String str1 = "3";
str1 = "4";

由于String类的对象是final类型的,这就意味着当执行str1 = "4"这条语句时jvm会在堆上重新开辟一个对象空间去指向字符串4的。而此时的str1就变成了另外一个新的String对象的引用了。这也就是String作为final类不能被修改的本质。

字符串常量池

在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。
8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:
直接使用双引号声明出来的String对象会直接存储在常量池中。
如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

在jdk1.7之前,字符串常量池是存在于jvm的方法区里的,从jdk1.7以后被移到堆里。下面叙述部分无特殊说明默认是jdk1.8环境下。

String str1 = new String("12");
String str2 = "2";

上面代码的第一句创建了两个对象:第一个对象是”12”字符串存储在常量池中,第二个对象在JAVA Heap中的 String 对象.此时str1指向的是JAVA Heap中的 String 对象,也就意味着获取str1的值要先获得堆中对象存储的值然后根据这个值再去常量池中获取字符串的值,一共两步。
而对于第二段代码,会将字符串"2"直接存储到常量池中,那么str2是常量池字符串对象的一个直接引用。

intern()方法

intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
下面代码来自于深入解析String#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);
}

结果为:
false
true
jdk1.7中4个对象的引用情况

分析:
1. 关于字符串s与s2做 == 判断为false:
String s = new String(“1”);这句话执行完后,s是堆里字符串对象的一个引用,并且此时字符串"1"已经存入字符串常量池中。
s.intern();执行过程是:由于字符串"1"已经在常量池中,所以不会去复制常量池对象的实例,也就是s保持原来的引用不变。
String s2 = “1”;这句胡的执行前文讲过,由于"1"已经存在于常量池中,所以s2是常量池中字符串"1"的引用。
所以s指向的是堆中非常量中的一个String对象,而s2指向的是常量池中字符串对象,因此 s == s2返回false
2. 关于字符串 s3 与s4做 == 判断为true:
String s3 = new String(“1”) + new String(“1”);这句话执行完之后,此时s3引用对象内容是”11”,但此时常量池中是没有 “11”对象的。
s3.intern();会将字符串"11"存入常量池中,然后将s3的引用改为常量池对象实例的引用。
String s4 = “11”;s4也是常量池字符串"11"的引用。
上述过程后s3与s4是同一个对象的两个引用,所以 s3 == s4返回true。

		String s = new String("1"); 
        String s2 = "1"; //常量池"1"的引用
        System.out.println(s == s2);

        String s3 = new String("1") + new String("1");
        String s4 = "11";
        System.out.println(s3 == s4);

结果:
false
false
上面的结果就很好理解了:s 和 s3都是堆对象的引用;s2和s4都是字符串常量池对象的引用。所以结果都为false。

总结

关于String对象 == 判断比较复杂,需要对字符串常量池熟练掌握同时还要对Java虚拟机内存区域分布熟练掌握,上文叙述的只是结合别人的分析做了一个总结。

感谢这篇文章:
https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值