String s = new String( "xyz")インスタンスはいくつ作成されますか?

見積もり

問題:Javaコード

String s = new String(“ xyz”);

いくつの文字列オブジェクトが作成されましたか?

この質問自体に対する合理的な答えはありません

見積もり

回答:2つ(1つは「xyz」でもう1つは「xyz」を指す参照オブジェクトです)
(まあ、この回答には多くの不満があります...誰もが気楽にやってくれます)

この問題の何が問題になっていますか?「作成された」の意味を定義するものではありません。
「作成された」とは何ですか?いつ作成されましたか?
そして、このJavaコードが実際に実行されるとき、それは本当に「2つのStringインスタンスを作成する」のでしょうか。

これが面接の質問である場合は、面接官に「作成された」の定義を直接明確にするように依頼し、それに応じて答えることができます。このとき、インタビュアーはインタビュイーに自分で説明してもらう可能性が高く、扱いやすく、インタビュアーに見せることができます。

筆記試験の場合、説明を求める機会はありません。しかし、このような問題が発生する場所のほとんどはあまり良くありません。たぶん、質問を書いた人がさまざまな本から質問をコピーしたので、本に従って間違った答えを書くことによってそれを混同することができます。

================================================== =====

別の質問に変更してみましょう。

Javaコード

String s = new String( "xyz");
実行時にいくつのStringインスタンスが関与しますか?

合理的な答えは次のとおりです。

回答:2つは、1つは文字列リテラル「xyz」に対応し、グローバルに共有される文字列定数プールに存在する(インターン)インスタンスです。もう1つは、新しいString(String)によって作成および初期化されます。例は、「xyz」と同じ内容です。 「」

「2020年の最新のJavaの基本と詳細なビデオチュートリアルおよび学習ルート!
これは、Java言語仕様の関連規定に従って与えることができる合理的な答えです。Java言語仕様が明確に述べていることを考慮すると:

Java言語仕様、第3版写道

Javaプログラミング言語は通常、Java仮想マシン仕様の第2版(Addison-Wesley、1999)で定義されているバイトコード化された命令セットとバイナリ形式にコンパイルされます。

つまり、Java言語は一般にJava仮想マシン仕様で定義されたクラスファイルにコンパイルされると規定されていますが、「必須」とは規定されておらず、JVMを使用せずにJava言語を実装する余地があります。
Java仮想マシンの仕様を考慮すると、このコードに含まれる定数型CONSTANT_String_infoは、実際には唯一の文字列定数「xyz」です。CONSTANT_String_infoは、Java言語で文字列型定数式(文字列リテラルを含む)の値を表すために使用される定数型です。このレベルのみを考慮すれば、この答えは問題ありません。
したがって、このソリューションは合理的であると見なすことができます。

質問の「実行時」には、クラスのロードフェーズとコードフラグメント自体の実行時間の両方が含まれることに注意してください。この詳細と元のポスターによって与えられた元の質問との関係については、以下で説明します。

この種の問題が発生した場合は、最初に関連する仕様を参照することを検討する必要があります。ここでは、Java言語仕様とJava仮想マシン仕様、およびいくつかの関連APIのJavaDocを示します。多くの人は「合理的」をキャッチフレーズと見なすのが好きです。規範はさまざまな「理由」を定義するために使用されます-「XXXはなぜYYYを意味するのですか?」「それは規範で定義されているからです!」-無敵。

Java仮想マシン仕様の関連する定義は次のとおりです。

Java仮想マシン仕様、第2版写道

2.3リテラル

リテラルは、プリミティブ型(§2.4.1)、文字列型(§2.4.8)、またはヌル型(§2.4)の値のソースコード表現です。文字列リテラル、より一般的には定数式の値である文字列は、メソッドString.internを使用して、一意のインスタンスを共有するように「インターン」されます。

null型には、リテラルnullで示されるnull参照という1つの値があります。ブール型には2つの値があり、リテラルtrueとfalseで示されます。

2.4.8クラス文字列

Stringクラスのインスタンスは、Unicode文字のシーケンスを表します(§2.1)。Stringオブジェクトには、一定の不変の値があります。文字列リテラル(§2.3)は、クラスStringのインスタンスへの参照です。

2.17.6新しいクラスインスタンスの作成

次のいずれかの状況が発生すると、新しいクラスインスタンスが明示的に作成されます。

クラスインスタンス作成式を評価すると、その名前が式に表示されるクラスの新しいインスタンスが作成されます。
クラスClassのnewInstanceメソッドを呼び出すと、メソッドが呼び出されたClassオブジェクトによって表されるクラスの新しいインスタンスが作成されます。

次の状況では、新しいクラスインスタンスが暗黙的に作成される場合があります。

文字列リテラルを含むクラスまたはインターフェイスをロードすると、そのリテラルを表す新しい文字列オブジェクト(§2.4.8)が作成される場合があります。Stringオブジェクトがそのリテラルの以前の出現を表すためにすでに作成されている場合、またはString.internメソッドがリテラルと同じ文字列を表すStringオブジェクトで呼び出されている場合、これは発生しない可能性があります。
定数式の一部ではない文字列連結演算子を実行すると、結果を表す新しいStringオブジェクトが作成されることがあります。文字列連結演算子は、プリミティブ型の値の一時ラッパーオブジェクトを作成することもできます(§2.4.1)。

これらの各状況は、クラスインスタンス作成プロセスの一部として、指定された引数(場合によってはなし)で呼び出される特定のコンストラクターを識別します。

5.1ランタイム定数プール

●文字列リテラル(§2.3)は、クラスまたはインターフェイスのバイナリ表現のCONSTANT_String_info構造(§4.4.3)から派生します。CONSTANT_String_info構造体は、文字列リテラルを構成するUnicode文字のシーケンスを提供します。

●Javaプログラミング言語では、同一の文字列リテラル(つまり、同じ文字シーケンスを含むリテラル)がクラスStringの同じインスタンスを参照する必要があります。さらに、メソッドString.internが任意の文字列で呼び出された場合、結果は、その文字列がリテラルとして表示された場合に返されるのと同じクラスインスタンスへの参照になります。したがって、
Java代码

(“ a” +“ b” +“ c”)。intern()==“ abc”

値はtrueである必要があります。

●文字列リテラルを導出するために、Java仮想マシンはCONSTANT_String_info構造体によって指定された文字のシーケンスを調べます。

○メソッドString.internが、CONSTANT_String_info構造で指定されたものと同一のUnicode文字のシーケンスを含むクラスStringのインスタンスで以前に呼び出された場合、文字列リテラルの派生の結果は、クラスStringの同じインスタンスへの参照になります。

○それ以外の場合は、CONSTANT_String_info構造で指定されたUnicode文字のシーケンスを含むクラスStringの新しいインスタンスが作成されます。そのクラスインスタンスは、文字列リテラルの派生の結果です。最後に、新しいStringインスタンスのinternメソッドが呼び出されます。

クラスまたはインターフェイスのバイナリ表現のconstant_poolテーブルの残りの構造であるCONSTANT_NameAndType_info(§4.4.6)およびCONSTANT_Utf8_info(§4.4.7)構造は、クラス、インターフェイス、メソッド、およびフィールド、および文字列リテラルを導出する場合。

SunのJDKをリファレンス実装(RI)と考えてください。String.intern()のJavaDocは次のとおりです。

JavaDocは書き込みます

public String intern()

文字列オブジェクトの正規表現を返します。

最初は空の文字列のプールは、Stringクラスによってプライベートに維持されます。

インターンメソッドが呼び出されたときに、equals(Object)メソッドによって決定されたこのStringオブジェクトに等しい文字列がプールにすでに含まれている場合、プールからの文字列が返されます。それ以外の場合、このStringオブジェクトはプールに追加され、このStringオブジェクトへの参照が返されます。

したがって、任意の2つの文字列sおよびtについて、s.equals(t)が真である場合に限り、s.intern()== t.intern()が真になります。

すべてのリテラル文字列と文字列値の定数式がインターンされます。文字列リテラルは、Java言語仕様の§3.10.5で定義されています

戻り値:
この文字列と同じ内容の文字列ですが、一意の文字列のプールからのものであることが保証されています。

================================================== =====

別の質問をする:

String s = new String( "xyz");
ユーザー宣言にはいくつの文字列型変数が関係していますか?

答えも非常に簡単です。

回答:1つは文字列です。

質問を次のバージョンに変更すると、答えは同じです。

String s = null;
ユーザーが宣言したいくつかのString型変数が含まれますか?

Javaの変数は変数です。参照型の変数は、インスタンス自体ではなく、オブジェクトインスタンスまたはnullへの単なる参照です。宣言された変数の数は、次のように、作成されたインスタンスの数と必ずしも関連していません。

String s1 = "a";
String s2 = s1.concat( "");
String s3 = null;
new String(s1);
このコードには
、次のStringインスタンス1
2を指す3つのString型変数1、s1が含まれます。。s2、s1
3、s3と同じものを指し、値はnullであり、インスタンスを指していません

そして3つの文字列インスタンス、

1.「a」リテラルに対応する常駐文字列定数のStringインスタンス

2. ""リテラルに対応する常駐文字列定数の文字列インスタンス

(String.concat()は興味深いメソッドです。渡されたパラメーターが空の文字列であることが検出されるとこれを返すため、追加のStringインスタンスはここに作成されません)

3. new String(String)によって作成された新しいStringインスタンス。それを指す変数はありません。

最初に引用した質問と「標準的な回答」に戻る

String s = new String( "xyz");
作成された文字列オブジェクトの数は?

回答:2つ(1つは「xyz」でもう1つは「xyz」を指す参照オブジェクト)
は還元主義によって示されます。質問が「このコードスニペットの実行中にいくつかの文字列インスタンスが作成された」と尋ねるとします。「標準的な答え」が正しければ、次のコードフラグメントは、実行時に4つのStringインスタンスを作成する必要があります。

String s1 = new String( "xyz");
String s2 = new String( "xyz");
誰かがすぐに飛び出して、上下の "xyz"リテラルが同じStringオブジェクトを参照していると言うので、そうすべきではありません。 4つのオブジェクトが作成されました。

いくつあるべきですか?

実行時のクラスのロードプロセスと特定のコードフラグメントの実際の実行については、意味のあるものにするために個別に説明する必要があります。

問題のコードフラグメントを実行するには、それが配置されているクラスを最初にロードする必要があり、同じクラスは最大で1回だけロードされます(JVMの場合、「同じクラス」では十分ではないことに注意してください。クラスの完全修飾名は同じですが、<クラスの完全修飾名、クラスローダーの定義>は同じペアです)。

上記の仕様の内容によると、仕様に準拠するJVM実装は、クラスのロードプロセス中に「xyz」リテラルに対応する定数としてStringインスタンスを作成して常駐させる必要があります。これは、特に解決時に実行されます。クラスのロードの段階。この定数はグローバルに共有され、同じ内容の文字列が以前に存在しなかった場合にのみ、新しい文字列インスタンスを作成する必要があります。

元の質問のコードフラグメントが実際に実行される場合、JVMが実行する必要のあるバイトコードは次のようになります。

Javaバイトコードコード:

0:new#2; // class java / lang / String
3:dup
4:ldc#3; // String xyz
6:invokespecial#4; //メソッドjava / lang / String。 "" :( Ljava / lang / String;)V
9:astore_1これに
新しいjava / lang / Stringが出現した回数は、作成されたStringオブジェクトの数です。つまり、元の質問のコードは、実行されるたびに新しいStringインスタンスを作成するだけです。
ここで、ldc命令は、クラスのロードプロセス中に作成されたStringオブジェクト( "xyz")の参照をオペランドスタックの最上位にプッシュするだけで、新しいStringオブジェクトは作成しません。

したがって、削減に使用されたコードスニペットは次のとおりです。

String s1 = new String( "xyz");
String s2 = new String( "xyz");
実行ごとに2つの新しいStringインスタンスのみが作成されます。


一部の学生が混乱するのを避けるために、私はもう一度強調したいと思います:

Java言語では、「new」式がインスタンスの作成を担当し、コンストラクターが呼び出されてインスタンスが初期化されます。コンストラクター自体の戻り値の型はvoidであり、「コンストラクターは新しく作成されたものへの参照を返します。オブジェクト "ですが、新しい式の値は新しく作成されたオブジェクトへの参照です。

これに対応して、JVMでは、「新しい」バイトコード命令は、インスタンスの作成(スペースの割り当て、タイプの設定、すべてのフィールドのデフォルト値の設定などを含む)、および新しく作成されたオブジェクトへの参照のプレスのみを担当しますスタックの最上位のオペランドに。現時点では、参照を直接使用することはできず、初期化されていない状態です。メソッドaに、初期化されていない状態の参照を介してインスタンスメソッドを呼び出そうとするコードが含まれている場合、メソッドaはJVMバイトコードの検証に失敗します。その結果、JVMは実行を拒否しました。

初期化されていない状態への参照で実行できる唯一のことは、インスタンスコンストラクターを呼び出すことです。これは、クラスファイルレベルで特別な初期化メソッド ""として表されます。実際の呼び出し命令はinvokespecialであり、実際の呼び出しの前に、必要なパラメーターをオペランドスタックに順番に押す必要があります。上記のバイトコードの例では、パラメーターを抑制するコマンドには、それぞれ2つのdupとldc、非表示パラメーター(新しく作成されたインスタンスの参照、たとえばコンストラクターは「this」)と最初に明示的に宣言された実際のパラメーター(a 「xyz」定数への参照)がオペランドスタックにプッシュされます。

コンストラクターが戻った後、新しく作成されたインスタンスの参照を通常どおりに使用できます。

接続アドレス:https://www.iteye.com/blog/rednaxelafx-774673

おすすめ

転載: blog.csdn.net/weixin_46699878/article/details/110688570