彻底搞懂string常量池和intern

先让大家做个面试题:

        String a = "hello";
        String b = new String("hello");
        System.out.println(a == b);//false

        String c = "world";
        System.out.println(c.intern() == c);//true
        
        String d = new String("mike");
        System.out.println(d.intern() == d);//false

        String e = new String("jo") + new String("hn");
        System.out.println(e.intern() == e);//true

        String f = new String("ja") + new String("va");
        System.out.println(f.intern() == f);//false

如果大家能一题不差的全做对,接下来的内容应该不用看了。如果不能并且有兴趣的话,可以稍微了解一下以下内容。

  • 字符串常量池

在上一篇文章已经稍微了解过字符串常量池了,它是java为了节省空间而设计的一个内存区域,java中所有的类共享一个字符串常量池。比如A类中需要一个“hello”的字符串常量,B类也需要同样的字符串常量,他们都是从字符串常量池中获取的字符串,并且获得得到的字符串常量的地址是一样的。

实际上,为了提高匹配速度,即更快的查找某个字符串是否存在于常量池,Java在设计字符串常量池的时候,还搞了一张stringtable,stringtable有点类似于我们的hashtable,里面保存了字符串的引用。我们可以根据字符串的hashcode找到对应entry,如果没冲突,它可能只是一个entry,如果有冲突,它可能是一个entry链表,然后Java再遍历entry链表,匹配引用对应的字符串,如果找得到字符串,返回引用,如果找不到字符串,会把字符串放到常量池中,并把引用保存到stringtable里。

  • 怎么样才能进入字符串常量池

往细讲,只有执行了ldc指令的字符串才会进入字符串常量池。什么意思呢?先看一个例子:

    public static void main(String[] args) {
        String a = "hello";
    }
Constant pool:
  #17 = String             #18            // hello
  #18 = Utf8               hello

{
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
Error: unknown attribute
      org.aspectj.weaver.MethodDeclarationLineNumber: length = 0x8
       00 00 00 10 00 00 00 BA
    Code:
      stack=1, locals=1, args_size=1
         0: ldc           #17                 // String hello
         2: pop
         3: return
      LineNumberTable:
        line 17: 0
        line 18: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;
}
SourceFile: "ClassPoolTest.java"

在这里0: ldc #17,#17对应的“hello”,如果字符串常量池中已经存在“hello”,java不会做什么事,但是如果字符串常量池中没有该字符串常量,java会把该字符串常量放到常量池中,并且把引用放到stringtable中。

  • 什么时候才会用到ldc指令

凡是有双引号括起字符串的地方就会用到ldc指令,比如上面的String a = "hello"。
我们再来看看几个例子:

public class ClassPoolTest {
    String a = "hello";
    public static void main(String[] args) {}
}

我们执行完main以后,hello不会进入字符串常量池。因为String a = "hello"是ClassPoolTest 的成员变量,成员变量只有在执行到构造方法的时候才会初始化。不信我们来看构造函数的反编译代码:

 public pool.ClassPoolTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #12                 // String hello
         7: putfield      #14                 // Field a:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 13: 0
        line 14: 4
        line 13: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lpool/ClassPoolTest;
public class ClassPoolTest {
    static String a = "hello";
    public static void main(String[] args) {}
}

执行完main以后,hello会进入常量池,因为static String a = "hello"是ClassPoolTest 静态变量,我们执行静态方法main之后张初始化静态变量,如下:

 static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #10                 // String hello
         2: putstatic     #12                 // Field a:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 14: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

可见hello已经被添加到字符串常量池中。

  • intern有什么用

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

我们继续看例子:

    public static void main(String[] args) {
        String a = new String("haha");
        System.out.println(a.intern() == a);//false
    }

因为有双引号括起来的字符串,所以会把ldc命令,即"haha"会被我们添加到字符串常量池,它的引用是string的char数组的地址,会被我们添加到stringtable中。所以a.intern的时候,返回的其实是string中的char数组的地址,和a的string实例化地址肯定是不一样的。

        String e = new String("jo") + new String("hn");
        System.out.println(e.intern() == e);//true

new String("jo") + new String("hn")实际上会转为stringbuffer的append 然后tosring()出来,实际上是new 一个新的string出来。在这个过程中,并没有双引号括起john,也就是说并不会执行ldc然后把john的引用添加到stringtable中,所以intern的时候实际就是把新的string地址(即e的地址)添加到stringtable中并且返回回来。

        String f = new String("ja") + new String("va");
        System.out.println(f.intern() == f);//false

或许很多朋友感觉很奇怪,这跟上面的例子2基本一模一样,但是却是false呢?这是因为java在启动的时候,会把一部分的字符串添加到字符串常量池中,而这个“java”就是其中之一。所以intern回来的引用是早就添加到字符串常量池中的”java“的引用,所以肯定跟f的原地址不一样。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 从网上复制的,看别人的比较全面,自己搬过来,方便以后查找。原链接:https://www.cnblogs.com/...
    lxtyp阅读 1,306评论 0 9
  • String是Java基础的重要考点。可问的点多,而且很多点可以横向切到其他考点,或纵向深入JVM。 本文略过了S...
    猴子007阅读 1,345评论 0 8
  •   需要说明的一点是,这篇文章是以《深入理解Java虚拟机》第二版这本书为基础的,这里假设大家已经了解了JVM的运...
    Geeks_Liu阅读 13,904评论 5 44
  • 转载请注明原创出处,谢谢!简书占小狼http://www.jianshu.com/users/90ab66c248...
    美团Java阅读 8,360评论 17 57
  • 杜杜是个神奇的人,口中的男朋友实际比她大十四岁,是个有点谢顶的中年律师,英国留学回来。在顶级的雷曼律师行工作。他们...
    雪泥999阅读 271评论 0 0