栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

字符串常量池 intern方法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

字符串常量池 intern方法

谢谢提醒,之前的文章写错了,但是还有一点不太清楚,解析字符串字面量时,究竟是先创建 String 对象,再到字符串常量池查找,还是先到字符串常量池查找,没有再创建 String 对象?

我觉得按正常人的思维,应是第 2 种,否则先创建 String 对象,万一字符串常量池已有值相同的字符串引用,就浪费了。当然也不排除第一种先创建 String 对象,才能调用 intern 方法的可能。不过底层多的是办法绕过去。

另外求职,意向软件测试实习,小白一枚。

之前自学 Java 时,都说好找工作,现在人太多了,风向又变了,说什么软件测试有很大缺口,赶紧来报培训班,之前面试也是,没想到是培训机构,反正作为不太优秀的我,就是被牵着鼻子走啊。

下面是正文:(肯定也有些小错误,但是契合已有例子问题不大)

字符串被使用前(执行 ldc 指令时)才去解析它。

String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);
# String s1 = "hello";

0: ldc #7 // String hello

2: astore_1

# String s2 = "hello";

3: ldc #7 // String hello
	
5: astore_2

所以 s1、s2 指向同一个对象,s1 == s2 为 true。

例 2:

String str = "abc";
String str2 = new String("abc");
System.out.println(str == str2);
# String str = "abc";

0: ldc #7 // String abc
2: astore_1
    
# String str2 = new String("abc");
	
3: new #9 // class java/lang/String
6: dup

7: ldc #7 // String abc
    
9: invokespecial #11 // Method java/lang/String."":(Ljava/lang/String;)V
12: astore_2
13: return

str、str2 指向的不是同一个对象,str == str2 为 false。


每个 Java 虚拟机必须维护一张内部列表 StringTable(内部结构是哈希表),也称字符串常量池,列出程序运行中已被 “拘留(intern)” 的字符串对象的引用,保证每个字符串对象在表中只出现一次。

可以通过 -XX:StringTableSize=60013 设置字符串常量池大小。


例 3:

String s1 = new String("hello");
String s2 = s1.intern();
System.out.println(s1 == s2);

JDK6 及之前,某引用调用 intern 方法时,若字符串常量池中已有指向值相同的字符串对象的引用,则直接返回池中引用。

若池中没有这样的引用,则在永久代中创建一个值相同的 String 对象,将引用拷贝到池中,并返回此引用。[5]

执行第一句代码,字符串常量池中无 “hello” 的引用,在永久代中创建 “hello” 对象,并将其引用放入池中,随后访问 “hello” 字面量都是返回此引用。

在堆中创建字符串对象,通过 init 方法传入 “hello” 引用完成实例变量的初始化,使得此字符串的值也为 “hello”。

然后将堆中对象的引用赋给 s1。

执行第 2 句代码,字符串常量池中已有值为 “hello” 的引用,直接返回此引用。

所以 s1 指向堆中的对象,s2 指向永久代的对象,s1 == s2 为 false。

String s1 = new String("hello");
String s2 = s1.intern();
System.out.println(s1 == s2);

JDK7 及以后,字符串常量池被移到堆中,intern 方法实现有所变化。

某引用调用 intern 方法时,之前部分一样…;若池中没有值相同的对象的引用,将此引用拷贝到池中,同时返回其引用。

所以过程同上,将永久代改为堆中。

例 4:

String s1 = new String(new char[] {'a', 'b', 'c'});// 值为 “abc” 的字符串
s1.intern();
String s2 = "abc";

JDK6:第 1 句代码在堆中创建了一个值为 “abc” 的字符串,引用赋给 s1。

第 2 句,池中无 “abc” 引用,于是在永久代中创建了一个新的 “abc” 对象,将新对象的引用放入池中,并返回此引用。

执行第 3 句代码时,在解析 “abc” 时,字符串常量池中已有值为 “abc” 引用,所以直接返回池中引用。所以 s1 == s2 为 false。

JDK7:第 2 句,池中无 “abc” 引用,将 s1 对象引用拷贝到池中,并返回此引用。

第 3 句,解析 “abc” 字面量时,在常量池中查找是否有 “hello” 的引用,发现有,直接返回池中引用。所以 s1 == s2 为 true。

将 String s1 = new String(new char[] {'a', 'b', 'c'}); 换成 String s1 = new String("a") + new String("bc"); 也是一样的效果,只要在 “abc” 字面量出现前,先让值为 “abc” 的引用加入常量池中就行。

例 5:

String s1 = new String(new char[] {'a', 'b', 'c'});// 值为 “abc” 的字符串
String s2 = "abc";

第一句在堆中创建了值为 “abc” 的字符串;

第二句,池中无 “abc” 引用,创建了一个新的 “abc” 对象。

所以 s1 == s2 为 false。

例 6:

final String s1 = "a";
String s2 = s1 + "bc";// 或换成 String s2 = "a" + "bc";
System.out.println("abc" == s2);

第二句对应的字节码指令:

0: ldc #7 // String abc
2: astore_1

对于只有常量和字面量的拼接,会自动优化成 String s2 = "abc";,所以 “abc” == s2 为 true。

若将 final 去掉,有变量参与的拼接,JDK8 及之前会调用 StringBuilder.append 方法拼接,最后调用 toString 方法在堆中创建 String 对象。

JDK8 后内部调用 java.lang.StringConcatHelper#simpleConcat 方法完成拼接,也是在堆中创建对象。

所以 “abc” == s2 为 false。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1003096.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号