8

JLS(15.28 Constant Expressions) によると、以下のみを含む式:

i)Literals of primitive type and literals of type String (§3.10.1, §3.10.2, §3.10.3,
§3.10.4, §3.10.5)
or
ii)Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
or
iii)...

定数式です。

NowString s1="a"+"b";は定数式であり"ab"、コンパイル時に評価されます。

それでs1="ab";

[1]上記のステートメントによると、文字列プールには 3 つのオブジェクトがあると言っても過言ではありません:-"a"、"b"、"ab"???

今、

final String s="a";
final String s1="b";
String s2=s+s1;  // is also constant expression and get evaluated at compile time.

上記のコードはs2="a"+"b";、コンパイル後に変換されます。

s2="ab";自動的に文字列プールに保存されます。

しかし、

// note there is no final now.
String s="a";
String s1="b";
String s2="a"+"b";  // constant expression.
String s3=s+s1;  // is NOT a constant expression and get evaluated at RUN TIME.

String s3=s+s1;場合、コードは次のように変換されます:

s3=new StringBuilder(String.valueOf(s)).append(s1).toString();

新しい String オブジェクトを作成します。

したがって、s2==s3will は偽であることがわかります。

StringBuilder を使用して実行時に評価された文字列連結の結果は、文字列プールに格納されず、代わりにヒープ(プール外)に入るということですか?

4

4 に答える 4

3

JLS §15.18.1 から:

15.18.1. 文字列連結演算子 +

1 つのオペランド式のみが String 型である場合、実行時に文字列を生成するために、もう一方のオペランドに対して文字列変換 (§5.1.11) が実行されます。

文字列連結の結果は、2 つのオペランド文字列を連結した String オブジェクトへの参照になります。新しく作成された文字列では、左側のオペランドの文字が右側のオペランドの文字よりも前になります。

式がコンパイル時の定数式 (§15.28) でない限り、String オブジェクトは新しく作成されます (§12.5)。

実装では、変換と連結を 1 つのステップで実行して、中間の String オブジェクトを作成してから破棄することを回避できます。文字列連結の繰り返しのパフォーマンスを向上させるために、Java コンパイラは StringBuffer クラスまたは同様の手法を使用して、式の評価によって作成される中間 String オブジェクトの数を減らすことができます。

プリミティブ型の場合、実装では、プリミティブ型から文字列に直接変換することで、ラッパー オブジェクトの作成を最適化することもできます。

そう、

  1. 定数プール ("ab") には 1 つのオブジェクトがあります。一時ファイルは保存されません。
  2. 同様に、定数プールには「ab」のみが存在します。
  3. 新しい文字列は新しいStringオブジェクトであり、明示的にインターンされない限りプールには含まれません。

いくつかのバイトコードを見ることは有益です:

String sa1 = "a"+ "b";

final String sb1 = "a";
final String sb2 = "b";
String sb3 = sb1 + sb2;

String sc1 = "a";
String sc2 = "b";
String sc3 = "a" + "b";
String sc4 = sc1 + sc2;

になる

  Code:
   0:   ldc #2; //String ab
   2:   astore_0
   3:   ldc #2; //String ab
   5:   astore_3
   6:   ldc #3; //String a
   8:   astore  4
   10:  ldc #4; //String b
   12:  astore  5
   14:  ldc #2; //String ab
   16:  astore  6
   18:  new #5; //class java/lang/StringBuilder
   21:  dup
   22:  invokespecial   #6; //Method java/lang/StringBuilder."<init>":()V
   25:  aload   4
   27:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   30:  aload   5
   32:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   35:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   38:  astore  7
   40:  return

最初の 2 つのケースでは、"ab" が定数プールから直接読み込まれていることがわかります。3 番目のブロックでsc4 = new StringBuilder().append(sc1).append(sc2).toString()は、新しいオブジェクトを作成する translation を取得します。

于 2012-10-14T15:44:59.800 に答える
1

上記のステートメントによると、文字列プールには 3 つのオブジェクトがあると言っても過言ではありません:-"a"、"b"、"ab"???

いいえ、あなたは間違っています。連結はコンパイル時に実行され、"ab" オブジェクトのみが文字列プールに格納されます。

StringBuilder を使用して実行時に評価された文字列連結の結果は、文字列プールに格納されず、代わりにヒープ(プール外)に入るということですか?

はい、あなたはこの点で正しいです。


要約すると、文字列リテラルとコンパイル時の定数文字列式の値はインターンされます (そして文字列プールに格納されます)。明示的に を呼び出さない限り、他の文字列連結の結果はインターンされませんString.intern()。(そして、めったにそうすべきではありません...文字列をインターンすることは、通常、良いことよりも害を及ぼすためです。)

いずれにせよ、==文字列の比較には使用しないでください。

于 2012-10-14T15:44:49.107 に答える
0

StringBuilder を使用して実行時に評価された文字列連結の結果は文字列プールに格納されず、代わりにヒープ(プール外)に入るということですか?

はい。それは正しいです。ヒープに新しいオブジェクトを作成します。

于 2012-10-14T15:44:16.173 に答える
0

はい、StringBuilder を使用して実行時に評価された文字列が文字列プールに格納されないことは正しいです。なぜなら:

  1. 演算子がありますnew(ヒープ内のオブジェクトに新しいメモリを割り当てます)
  2. そして、コードからわかるように:

    AbstractStringBuilder(int 容量) {値 = 新しい文字 [容量]; }

ヒープに割り当てられた新しい char 配列参照があります。

于 2012-10-14T15:45:30.620 に答える