1

多くのフォーラムでこれに関する多くの質問がありますが、それらすべてを読むと、実際に最初の場所に戻ることができます!! 私が理解している限り、以下の例では:

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

...3 つのオブジェクトを作成しますよね? "abc" (参照が割り当てられていないため失われる)、"xyz" (失われる) および "abcxyz"

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

...2 つの文字列オブジェクトを作成します。「def」と new 演算子を使用したもの

1 については、コンパイル時に文字列の連結が解決され、「abcxyz」というオブジェクトが 1 つだけ作成されると聞いています。

2 については、new を使用すると、そのデータを含む char[] が作成され、作成されるオブジェクトの数が増えることがあると聞きました!!

これが正しいかどうか教えてください。

4

3 に答える 3

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

連結がコンパイル時の定数式である場合、連結はコンパイラによって実行され、結果の String が String プールに追加されます。JLS 3.10.5を参照

長い文字列リテラルは常に短い断片に分割し、文字列連結演算子 + [...] を使用して (場合によっては括弧で囲まれた) 式として記述することができます。さらに、文字列リテラルは常にクラス String の同じインスタンスを参照します。

- 定数式 (§15.28) によって計算される文字列は、コンパイル時に計算され、リテラルであるかのように扱われます。

-実行時に連結によって計算された文字列は新しく作成されるため、区別されます。

文字列連結演算子 + (§15.18.1) は、結果がコンパイル時の定数式 (§15.28) でない場合、暗黙的に新しい String オブジェクトを作成します。

String s = new String("def"); 

よくわかりませんが、ここintern()で使用されているようです.JVMは最初に文字列リテラルを内部プールにあるかどうかを強制的に検索し、そこにある場合Stringは同じ文字シーケンスで新しいオブジェクトを作成するか、そうでない場合はオブジェクトを作成しますプールとヒープ内の別のオブジェクト。

于 2013-07-17T04:25:01.447 に答える
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
}

これがより理にかなっているように、アセンブリについて少し知っていることを願っています。ウィキペディアでバイトコード命令の詳細を読むことができます。

何が起こっているのですか:

  1. 最初の例では、コンパイラはコンパイル時に 2 つの定数文字列の連結を最適化します。連結された文字列は、テキスト定数 #18 を参照する"abcxyz"文字列としてクラス ファイルの「定数プール」に格納されます。#2メソッドが行うone()ことは、定数文字列を返すことだけです。オブジェクトは、クラスがロードされるときに自動的に作成されます。文字列は不変であるため、メソッドを何度呼び出しても、追加のオブジェクトが作成されることはありませんone()。したがって、この特殊なケースの答えは、そのコード行が実行されるたびに作成されるオブジェクトはゼロです。

  2. 2 番目の例では、行が実行されるたびに、1 つの新しいStringオブジェクトが明示的に作成され、String()コンストラクターが明示的に呼び出されます。ただし、"def"コンストラクターへの引数は、クラスが読み込まれるときに作成される読み取り専用メモリである定数プールから取得されるため、追加のchar[]オブジェクトは作成されません。JLSでは新しいオブジェクトを作成する必要があるnewため、JVM がオブジェクトの作成を最適化できないと思います。

于 2013-07-17T05:49:34.560 に答える