与えられた問題で作成されるオブジェクトの数についてインタビューで尋ねられました:
String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;
文字列プールに6オブジェクトが作成されると答えました。
3は3つの変数のそれぞれに対応します。
1はstr1 + str2
(str
としましょう)。
1はstr2 + str3
。
1はstr + str3
(str = str1 + str2
)。
私が出した答えは正しいですか?そうでない場合、正しい答えは何ですか?
あなたの質問に対する答えは、JVMの実装と現在使用されているJava=バージョンによって異なります。インタビューで尋ねるのは不合理な質問だと思います。
私のマシンでは、Java 1.8.0_201の場合、スニペットはこのバイトコードになります
_L0
LINENUMBER 13 L0
LDC "First"
ASTORE 1
L1
LINENUMBER 14 L1
LDC "Second"
ASTORE 2
L2
LINENUMBER 15 L2
LDC "Third"
ASTORE 3
L3
LINENUMBER 16 L3
NEW Java/lang/StringBuilder
DUP
INVOKESPECIAL Java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL Java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 4
_
これは、5個のオブジェクトが作成されていることを証明します(3 String
literals *、1 StringBuilder
=、 _StringBuilder#toString
_ )によって動的に生成されたString
インスタンス。
私のマシンでは、Java 12.0.2で、バイトコードは
_// identical to the bytecode above
L3
LINENUMBER 16 L3
ALOAD 1
ALOAD 2
ALOAD 3
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
Java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001\u0001"
]
ASTORE 4
_
which magically は、「正しい答え」を4個のオブジェクトに変更します。これは、中間のStringBuilder
が含まれていないためです。
*もう少し掘り下げましょう。
12.5。新しいクラスインスタンスの作成
次の状況では、新しいクラスインスタンスが暗黙的に作成される場合があります。
- 文字列リテラル( §3.10.5 )を含むクラスまたはインターフェースをロードすると、リテラルを表す新しいStringオブジェクトが作成される場合があります。 (これは、同じUnicodeコードポイントのシーケンスを示す文字列が以前にインターンされている場合は発生しません。)
つまり、アプリケーションを起動すると、文字列プールには既にオブジェクトが存在します。あなたはそれらが何であるか、そしてそれらがどこから来るのかほとんど理解していません(あなたがそれらに含まれるすべてのリテラルについてすべてのロードされたクラスをスキャンしない限り)。
_Java.lang.String
_クラスは間違いなく必須のJVMクラスとしてロードされます。つまり、そのすべてのリテラルが作成され、プールに配置されます。
String
のソースコードからランダムに選択されたスニペットを取得し、そこからいくつかのリテラルを選択し、プログラムの最初にブレークポイントを配置し、プールにこれらのリテラルが含まれているかどうかを調べます。
_public final class String
implements Java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
...
public String repeat(int count) {
// ...
if (Integer.MAX_VALUE / count < len) {
throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
" times will produce a String exceeding maximum size.");
}
}
...
}
_
彼らは確かにそこにいます。
興味深い発見として、このIDEAのフィルタリングには副作用があります。私が探していた部分文字列もプールに追加されました。 this.contains("bytes String")
を適用した後、プールサイズが1増加しました(_"bytes String"
_が追加されました)。
これはどこに私たちを残しますか?
_"First"
_を呼び出す前に_String str1 = "First";
_が作成され、インターンされたかどうかはわかりません。そのため、この行が新しいインスタンスを作成することを明確に述べることはできません。
与えられた情報では、質問に明確に答えることはできません。 JLS、§15.18.1 で述べられているように:
...繰り返される文字列連結のパフォーマンスを向上させるために、Javaコンパイラは、
StringBuffer
クラスまたは同様の手法を使用して、式の評価。
これは、答えが少なくとも具体的なJava使用されるコンパイラに依存することを意味します。
私たちができる最善のことは、答えとして間隔を与えることです:
str1
からstr3
が使用されないことを推測し、String
- objectが1つだけ作成されるように、コンパイル中に連結を折りたたむことができます(str4
によって参照されるオブジェクト)String
sの実用的な最大数は5です。str1
からstr3
まで1つずつ、tmp = str1 + str2
用に1つ、str4 = tmp + str3
用に1つ。だから...私の答えは「1〜5個のString
- objectsの間の何か」でしょう。この操作のために作成されたオブジェクトの総数については...わかりません。これは、たとえば、 StringBuffer
が実装されています。
余談ですが、そのような質問をする背後にある理由は何でしょうか。通常、これらの詳細を気にする必要はありません。
Java 8はおそらく5つのオブジェクトを作成します:
StringBuilder
String
の場合は1Java 9 things changed を使用すると、String
連結はStringBuilder
を使用しなくなります。
5である必要があります。
3つのリテラルに3つ(str1
、str2
およびstr3
に割り当てられます)
str1 + str2
用に1つ
(result from the previous operation) + str3
用に1つ(str4
に割り当て済み)
適合Java実装は、実行時またはコンパイル時に、文字列をいくつでも連結できます。結果がそうでないことを検出した場合はゼロオブジェクトを含む、任意の数の実行時オブジェクトが必要です。実行時に必要です。
4つの文字列オブジェクトが文字列定数プールに作成されます。リテラルの場合は3、連結の場合は1。
私たちが使うなら
String s1 = new String("one")
2つのオブジェクトが1つは定数プールに、もう1つはヒープメモリに作成されます。
定義すると:
String s1 = "one";
String s2 = new String("one");
2つのオブジェクトが1つは定数プールに、もう1つはヒープメモリに作成されます。
連結操作では、これらの多くのStringオブジェクトは作成されません。 a StringBuilder
を作成し、文字列を追加します。したがって、5つのオブジェクト、3(変数)+ 1(sb)+ 1(連結文字列)が存在する可能性があります。