1

在许多论坛中有很多关于此的问题,但是当您阅读每一个问题时,您实际上又回到了起点!据我了解,以下示例:

1. String s = "abc" + "xyz"; 

...会创建 3 个对象吗?“abc”(由于没有分配参考而丢失),“xyz”(丢失)和“abcxyz”

2. String s = new String("def"); 

...将创建 2 个字符串对象。"def" 和带有 new 运算符的那个

对于 1,我听说编译时解决了字符串连接问题,并且只创建了 1 个对象“abcxyz”

对于 2,我听说当我们使用 new 时,有时会创建包含其数据的 char[],这会增加创建的对象数量!!

请让我知道这是否正确。

4

3 回答 3

1

如果您想准确查看编译器生成的内容,请使用以下命令从.class文件中转储字节码:javap

$ cat Foo.java
public class Foo {
    public String one() {
        String s = "abc" + "xyz";
        return s;
    }

    public String two() {
        String s = new String("def");
        return s;
    }
}
$ javac Foo.java
$ javap -verbose Foo.class
Classfile /Users/andrew/projects/sx/17690890/Foo.class
  Last modified 16-Jul-2013; size 393 bytes
  MD5 checksum d5818f15e34097180729ce7c3055594e
  Compiled from "Foo.java"
public class Foo
  SourceFile: "Foo.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#17         //  java/lang/Object."<init>":()V
   #2 = String             #18            //  abcxyz
   #3 = Class              #19            //  java/lang/String
   #4 = String             #20            //  def
   #5 = Methodref          #3.#21         //  java/lang/String."<init>":(Ljava/lang/String;)V
   #6 = Class              #22            //  Foo
   #7 = Class              #23            //  java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               one
  #13 = Utf8               ()Ljava/lang/String;
  #14 = Utf8               two
  #15 = Utf8               SourceFile
  #16 = Utf8               Foo.java
  #17 = NameAndType        #8:#9          //  "<init>":()V
  #18 = Utf8               abcxyz
  #19 = Utf8               java/lang/String
  #20 = Utf8               def
  #21 = NameAndType        #8:#24         //  "<init>":(Ljava/lang/String;)V
  #22 = Utf8               Foo
  #23 = Utf8               java/lang/Object
  #24 = Utf8               (Ljava/lang/String;)V
{
  public Foo();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 1: 0

  public java.lang.String one();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // String abcxyz
         2: astore_1      
         3: aload_1       
         4: areturn       
      LineNumberTable:
        line 3: 0
        line 4: 3

  public java.lang.String two();
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #3                  // class java/lang/String
         3: dup           
         4: ldc           #4                  // String def
         6: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1      
        10: aload_1       
        11: areturn       
      LineNumberTable:
        line 8: 0
        line 9: 10
}

希望你知道一点汇编,这样就更有意义了。您可以在 Wikipedia 上阅读有关字节码指令的更多详细信息。

发生了什么是:

  1. 在第一个示例中,编译器在编译时优化了两个常量字符串的连接。连接的字符串作为引用文本常量#18的字符串"abcxyz"存储在类文件的“常量池”中。#2one()方法所做的只是返回常量字符串。加载类时会自动创建对象,并且由于字符串是不可变的,因此无论您调用该one()方法多少次,都不会创建其他对象。因此,在这种特殊情况下,答案是每次执行该行代码时都会创建零个对象。

  2. 在第二个示例中,每次执行该行时,都会显式创建一个String对象并显String()式调用构造函数。但是,"def"构造函数的参数来自常量池,它是加载类时创建的只读内存,因此不会char[]创建额外的对象。JLS要求new创建一个新对象,因此我认为 JVM 无法优化对象创建。

于 2013-07-17T05:49:34.560 回答
1

您迷失在实施细节中。显然,您想了解,但我建议您首先真正掌握 Objects。

如果你坚持基础,那么 1. 只有一个对象 s 和两个字面量;2. 只有一个对象,s。

实施细节可以(并且确实)改变。深层概念要稳定得多。

于 2013-07-17T04:17:18.207 回答
1
String s = "abc" + "xyz"; 

如果连接是编译时常量表达式,它将由编译器执行,并将生成的字符串添加到字符串池中。参考JLS 3.10.5

长字符串字面量总是可以分解成更短的部分,并使用字符串连接运算符 + [...] 写成(可能带括号的)表达式。此外,字符串字面量总是引用 String 类的同一个实例。

- 由常量表达式(第 15.28 节)计算的字符串在编译时计算,然后将它们视为文字。

- 在运行时通过连接计算的字符串是新创建的,因此是不同的。

当结果不是编译时常量表达式(第 15.28 节)时,字符串连接运算符 +(第 15.18.1 节)隐式创建一个新的字符串对象。

String s = new String("def"); 

我不确定,但它似乎intern()在这里使用。JVM 强制首先搜索内部池中是否存在任何字符串文字,如果存在则它将创建一个String具有相同字符序列的新对象,否则它将创建一个对象池和堆中的另一个对象。

于 2013-07-17T04:25:01.447 回答