【JVM之内存与垃圾回收篇】StringTable (4)

另一个对象:字符串常量池中的对象

new String("a") + new String("b") 会创建几个对象 /** * new String("ab") 会创建几个对象? 看字节码就知道是2个对象 * * @author: Nemo */ public class StringNewTest { public static void main(String[] args) { String str = new String("a") + new String("b"); } }

字节码文件为

0 new #2 <java/lang/StringBuilder> 3 dup 4 invokespecial #3 <java/lang/StringBuilder.<init>> 7 new #4 <java/lang/String> 10 dup 11 ldc #5 <a> 13 invokespecial #6 <java/lang/String.<init>> 16 invokevirtual #7 <java/lang/StringBuilder.append> 19 new #4 <java/lang/String> 22 dup 23 ldc #8 <b> 25 invokespecial #6 <java/lang/String.<init>> 28 invokevirtual #7 <java/lang/StringBuilder.append> 31 invokevirtual #9 <java/lang/StringBuilder.toString> 34 astore_1 35 return

我们创建了 6 个对象

对象 1:new StringBuilder()

对象 2:new String("a")

对象 3:常量池的 a

对象 4:new String("b")

对象 5:常量池的 b

对象 6:toString中会约等于创建一个 new String("ab")

调用 toString 方法,不会在常量池中生成"ab"(而是会生成"a"和"b"),因为并没有声明"ab"常量。当然,如果是 new String("ab") 那常量池肯定有常量。

intern 的使用:JDK6 和 JDK7 JDK6 中 String s = new String("1"); // 在常量池中已经有了,"1"常量放到常量池,new对象放到堆 s.intern(); // 将该对象放入到常量池。但是调用此方法没有太多的区别,因为已经存在了1 String s2 = "1"; System.out.println(s == s2); // false String s3 = new String("1") + new String("1");//s3变量记录的地址为:new String("11") //执行完上一行代码以后,字符串常量池中,不存在"11" s3.intern();//在字符串常量池中生成"11"。如何理解:jdk6中创建了一个新的对象"11",也就有新的地址 //jdk7:此时常量池中并没有创建"11",而是创建了一个指向堆空间中new String("11") 的地址 String s4 = "11";//s4变量记录的地址:使用的是上一行代码执行时,在常量池中生成的"11"地址 System.out.println(s3 == s4); // false

输出结果

false false

为什么对象会不一样呢?
String s = new String("1");"1" 常量放到常量池,new 对象放到堆
String s2 = "1";时,去检查常量池,发现有 "1",直接返回了常量池的引用,没有创建对象。
对于 s2 字符串来说,它的创建过程同上所说。在创建该对象之前,JVM 会在 String 对象池中去搜索该字符对象是否已经被创建,如果已经被创建,则直接返回一个引用,否则先创建在返回引用。
而 s 字符串变量,它的创建过程就要多一个步骤。除了类似于 str2 字符串对象创建过程以外,它还会额外的创建一个新的 String 对象,也就是 new 关键字的作用,并且返回一个引用给 s。

一个是 new 创建的对象,是堆空间中的地址

一个是字面量赋值,是常量池中的对象,是常量池从的地址,显然不是同一个

【JVM之内存与垃圾回收篇】StringTable

如果是下面这样的,那么就是 true

String s = new String("1"); s = s.intern(); String s2 = "1"; System.out.println(s == s2); // true

而对于下面的来说,因为 s3 变量记录的地址是 new String("11"),然后这段代码执行完以后,常量池中不存在 "11",这是 JDK6 的关系,然后执行 s3.intern() 后,就会在常量池中生成 "11",最后 s4 用的就是 s3 的地址

为什么最后输出的 s3 == s4 会为false呢?

这是因为在 JDK6 中创建了一个新的对象 "11",也就是有了新的地址,s2 = 新地址

而在 JDK7 中,在 JDK7 中,并没有创新一个新对象,而是指向常量池中的新对象

JDK7 中 String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); // false String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); // true

【JVM之内存与垃圾回收篇】StringTable

扩展 String s3 = new String("1") + new String("1"); String s4 = "11"; // 在常量池中生成的字符串 s3.intern(); // 然后s3就会从常量池中找,发现有了,就什么事情都不做 System.out.println(s3 == s4);

我们将 s4 的位置向上移动一行,发现变化就会很大,最后得到的是 false

总结

总结 string 的 intern() 的使用:

JDK1.6 中,将这个字符串对象尝试放入串池(字符串常量池)。

如果串池中有,则并不会放入。返回已有的串池中的对象的地址

如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址

JDK1.7 起,将这个字符串对象尝试放入串池。

如果串池中有,则并不会放入。返回已有的串池中的对象的地址

如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjgfw.html