6

我和我的朋友正在讨论字符串,我们坚持这个:

String str = "ObjectOne"+"ObjectTwo";

他说总共将创建三个对象,我说将创建一个对象。

他对 3 个对象的逻辑是:一个用于“ObjectOne”,一个用于“ObjectTwo”,第三个是两个 String 对象的连接版本。

我在一个对象背后的逻辑是在编译时两个字符串对象将在字节码中连接为:

String str = "ObjectOneObjectTwo";  

并且在运行时只会以这种方式创建一个对象。这背后的真相是什么。

4

6 回答 6

11

如果你写(文字或常量)

String str = "ObjectOne"+"ObjectTwo";

相当于

String str = "ObjectOneObjectTwo"; // compiler optimize it so one Object
于 2013-04-02T09:36:51.100 回答
7

您可以通过使用该javap工具反汇编代码来自己发现这一点,看看编译器是用它做什么的。假设你有这个例子:

public class Example {
    public static void main(String[] args) {
        String s = "ObjectOne" + "ObjectTwo";
        System.out.println(s);
    }
}

编译它,然后用javap -c Example. 结果是:

Compiled from "Example.java"
public class Example {
  public Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String ObjectOneObjectTwo
       2: astore_1
       3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       6: aload_1
       7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      10: return
}

如您所见,只有一个String对象,其中包含“ObjectOneObjectTwo”。因此,确实,编译器在编译时为您进行了连接。

于 2013-04-02T09:42:03.893 回答
1

您可以自己轻松检查:

  1. 编译以下程序:

    public static void main(String[] args) {
        String str = "ObjectOne"+"ObjectTwo";
        System.out.println(str);
    }
    
  2. 检查编译器发出的字节码:

    javap.exe -v Test.class
    

对于 main 方法,将打印:

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC

    Code:
      stack=2, locals=2, args_size=1
         0: ldc           #16                 // String ObjectOneObjectTwo
         2: astore_1      
         3: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
         6: aload_1       
         7: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        10: return        
      LineNumberTable:
        line 6: 0
        line 7: 3
        line 8: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      11     0  args   [Ljava/lang/String;
               3       8     1   str   Ljava/lang/String;
}

如您所见,程序使用ldc字节码指令来引用单个已加载的字符串实例(在 Test.class 时加载)。因此,在执行该行期间不会创建新对象。

Java 语言规范要求编译器执行此优化:

String 对象是新创建的(第 12.5 节),除非表达式是编译时常量表达式(第 15.28 节)。

于 2013-04-02T09:54:19.893 回答
0
String str1 = "ObjectOne";
String str2 = "ObjectTwo";
str1 = str1+str2;

在上述情况下,将创建三个对象。

但是当你定义像

String str = "ObjectOne"+"ObjectTwo";

那么只会创建一个对象。编译器对其进行优化。

于 2013-04-02T09:57:49.477 回答
0

字符串对象是不可变的。

    String str = "ObjectOne"+"ObjectTwo"; 

         is same as

    String str = "ObjectOneObjectTwo"; 

不可变是指存储在 String 对象中的值不能更改。然后我们想到的下一个问题是“如果 String 是不可变的,那么我如何能够随时更改对象的内容?” . 好吧,准确地说,反映您所做更改的不是同一个 String 对象。在内部创建了一个新的 String 对象来进行更改。

所以假设你声明了一个 String 对象:

 String myString = "Hello";

接下来,您要将“Guest”附加到同一字符串。你做什么工作?

myString = myString + " Guest";

当您打印 myString 的内容时,输出将是“Hello Guest”。尽管我们使用了相同的对象(myString),但在内部过程中创建了一个新对象。所以 mystring 将引用“Hello Guest”。对 hello 的引用丢失了。

  String s1 = "hello"; //case 1 
  String s2 = "hello"; //case 2 

在情况 1 中,文字 s1 是新创建的并保存在池中。但是在情况 2 中,文字 s2 引用 s1,它不会创建新的。

if(s1 == s2) System.out.println("equal"); //打印相等

      String s= "abc"; //initaly s refers to abc
      String s2 =s;    //s2 also refers to abc
      s=s.concat("def"); // s refers to abcdef. s no longer refers to abc.

在此处输入图像描述

于 2013-04-03T08:14:41.117 回答
-3
String str = "ObjectOne"+"ObjectTwo";

3 个对象将被创建为

1-“对象一”

2-“对象二”

3-“ObjectOneObjectTwo”

采用

StringBuffer tmp = new StringBuffer("ObjectOne");
tmp.append("ObjectTwo");
str = tmp.toString();
于 2013-04-02T09:44:20.817 回答