java – 字符串实习谜语

我正在与我的同事的下一个谜语挣扎:
public class App1 {
  public static void main(String[] args) {
    String s1 = "Ja".concat("va"); // seems to be interned?!
    String s2 = s1.intern();

    System.out.println(s1 == s2); // true
  }
}

输出是真的.我有点惊讶,因为看起来s1是实习生.但这不是常数表达,不是吗?

但后来我更加惊讶为什么以下打印错误.

public class App2 {
  public static void main(String[] args) {
    String s1 = "Ja".concat("va"); // seems not to be interned?!
    String s3 = new String("Java"); // this changes output
    String s2 = s1.intern();

    System.out.println(s1 == s2); // false
  }
}

为什么引入s3会改变输出

解决方法

以下是管理Java String对象wrt到String池的规则:

>使用String文字创建String对象时,JVM会检查池中是否已存在字符串文字.如果池中存在该对象,则返回相同的对象而不是新对象.
>使用new运算符创建String对象时,即使字符串池中存在该字符串,也会创建新对象.
>在String对象上调用intern方法时,会创建一个新的String对象并将其放在池中(如果该对象不存在). intern方法从池中返回对象.

让我们回顾一下你的例子,

String s1 = "Ja".concat("va");

如果你看一下String source中的concat操作,你会注意到它最后会调用new运算符.

new String(buf,true)

因此,s1不会添加到字符串池中.

现在,让我们看一下实习生所在的行,

String s2 = s1.intern();

这里,s1上的intern方法从String池返回该对象(如果它不存在则创建).因此,s2包含String池中的对象.

同时,s1仍然包含旧对象而不是池中的对象.因此,
(s1 == s2)
永远都会回归虚假.

Java 1.8.0_92-b14中的修改行为

Java 8中的行为已更改. Java编译器正在执行优化.如果在concat之后立即调用intern方法,Java 8将优化并在字符串池中创建字符串对象,并忽略我们在早期Java版本中见过的new的早期行为.请检查反编译代码的操作码中的优化(checkOne是App1,checkTwo是App2),

  public static void checkOne();
    descriptor: ()V
    flags: ACC_PUBLIC,ACC_STATIC
    Code:
      stack=3,locals=2,args_size=0
         0: ldc           #2                  // String Ja
         2: ldc           #3                  // String va
         4: invokevirtual #4                  // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
         7: astore_0
         8: aload_0
         9: invokevirtual #5                  // Method java/lang/String.intern:()Ljava/lang/String;
        12: astore_1
        13: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: aload_0
        17: aload_1
        18: if_acmpne     25
        21: iconst_1
        22: goto          26
        25: iconst_0
        26: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        29: return
      LineNumberTable:
        line 6: 0
        line 7: 8
        line 9: 13
        line 10: 29
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            8      22     0    s1   Ljava/lang/String;
           13      17     1    s2   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 25
          locals = [ class java/lang/String,class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class java/lang/String,class java/lang/String ]
          stack = [ class java/io/PrintStream,int ]

  public static void checkTwo();
    descriptor: ()V
    flags: ACC_PUBLIC,locals=3,args_size=0
         0: ldc           #2                  // String Ja
         2: ldc           #3                  // String va
         4: invokevirtual #4                  // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
         7: astore_0
         8: new           #8                  // class java/lang/String
        11: dup
        12: ldc           #9                  // String Java
        14: invokespecial #10                 // Method java/lang/String."":(Ljava/lang/String;)V
        17: astore_1
        18: aload_0
        19: invokevirtual #5                  // Method java/lang/String.intern:()Ljava/lang/String;
        22: astore_2
        23: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        26: aload_0
        27: aload_2
        28: if_acmpne     35
        31: iconst_1
        32: goto          36
        35: iconst_0
        36: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        39: return
      LineNumberTable:
        line 13: 0
        line 14: 8
        line 15: 18
        line 17: 23
        line 18: 39
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            8      32     0    s1   Ljava/lang/String;
           18      22     1    s3   Ljava/lang/String;
           23      17     2    s2   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 35
          locals = [ class java/lang/String,class java/lang/String,int ]

相关文章

ArrayList简介:ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增...
一、进程与线程 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。 线程...
本文为博客园作者所写: 一寸HUI,个人博客地址:https://www.cnblogs.com/zsql/ 简单的一个类...
#############java面向对象详解#############1、面向对象基本概念2、类与对象3、类和对象的定义格式4、...
一、什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错。在java中,阻止当前方法或作用域...
Collection接口 Collection接口 Collection接口 Collection是最基本的集合接口,一个Collection代表一组...