Javaで文字列が不変なのはなぜですか?


78

その理由を理解できませんでした。私は常に他の開発者のようにStringクラスを使用しますが、その値を変更すると、Stringの新しいインスタンスが作成されます。

JavaのStringクラスの不変性の理由は何ですか?

StringBufferやStringBuilderのような代替手段があることを知っています。それは単なる好奇心です。


20
技術的には、重複していないですが、エリックリペットは、ここでは、この質問への偉大な答えを与える:programmers.stackexchange.com/a/190913/33843
Heinzi

回答:


105

並行性

Javaは最初から並行性を考慮して定義されました。よく言及されるように、共有されたミュータブルには問題があります。あることは、別のスレッドの背後で別のスレッドを変更することができます。

共有文字列のために発生した多数のマルチスレッドC ++バグがあります。コード内の別のモジュールがポインターを保存し、同じままであると予想したときに、あるモジュールが変更しても安全だと判断した場合です。

これに対する「解決策」は、すべてのクラスがそれに渡される可変オブジェクトの防御コピーを作成することです。可変文字列の場合、これはコピーを作成するO(n)です。不変の文字列の場合、コピーを作成することはO(1)です。これはコピーではなく、変更できない同じオブジェクトだからです。

マルチスレッド環境では、不変オブジェクトを常に安全に共有できます。これにより、メモリ使用量が全体的に削減され、メモリキャッシュが向上します。

セキュリティ

多くの場合、コンストラクターへの引数として文字列が渡されます-最も簡単に思いつくのは、ネットワーク接続とプロトコルです。実行後の不特定の時点でこれを変更できると、セキュリティの問題につながる可能性があります(この機能は、あるマシンに接続していると考えていましたが、別のマシンに流用されましたが、オブジェクトのすべてが最初のマシンに接続されているように見えます...その同じ文字列)。

Javaではリフレクションを使用できます。このためのパラメーターは文字列です。反映する別のメソッドへの途中で変更される可能性のある文字列を渡す危険性 これは非常に悪いです。

ハッシュの鍵

ハッシュテーブルは、最もよく使用されるデータ構造の1つです。多くの場合、データ構造のキーは文字列です。不変の文字列があるということは、(上記のように)ハッシュテーブルが毎回ハッシュキーのコピーを作成する必要がないことを意味します。文字列が可変であり、ハッシュテーブルがこれを行わなかった場合、ハッシュキーをある距離で変更できる可能性があります。

javaのオブジェクトが機能する方法は、すべてがハッシュキーを持っていることです(hashCode()メソッドを介してアクセスされます)。不変の文字列を持つことは、hashCodeをキャッシュできることを意味します。文字列がハッシュのキーとして使用される頻度を考慮すると、これによりパフォーマンスが大幅に向上します(毎回ハッシュコードを再計算する必要がなくなります)。

部分文字列

Stringを不変にすることにより、データ構造を支える基になる文字配列も不変になります。これにより、substringメソッドの特定の最適化を実行できます(必ずしも実行されるとは限りません-また、いくつかのメモリリークの可能性が生じます)。

もしあなたがそうするなら:

String foo = "smiles";
String bar = foo.substring(1,5);

の値barは「マイル」です。ただし、両方foobar同じ文字配列でサポートし、文字配列内の異なる開始点と終了点を使用するだけで、より多くの文字配列のインスタンス化を減らすか、コピーすることができます。

foo | | (0、6)
    vv
    笑顔
     ^ ^
バー| | (1、5)

さて、その欠点(メモリリーク)は、1kの長さの文字列があり、最初と2番目の文字の部分文字列を取得した場合、1kの長さの文字配列によって裏付けられることです。文字配列全体の値を持つ元の文字列がガベージコレクションされた場合でも、この配列はメモリに残ります。

JDK 6b14の文字列でこれを見ることができます(次のコードはGPL v2ソースからのものであり、例として使用されています)

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

部分文字列がパッケージレベルのStringコンストラクターを使用する方法に注意してください。このコンストラクターは配列のコピーを伴わず、はるかに高速になります(大規模な配列を複製することはありませんが、大規模な配列を保持する可能性があります)。

上記のコードはJava 1.6用であることに注意してください。サブストリングコンストラクターの実装方法は、Java 1.7.0_06で行われた「ストリング内部表現の変更」に記載されているように、Java 1.7で変更されました -上記のメモリリークに関する問題。Javaは、多くの文字列操作を持つ言語とは見なされなかったため、部分文字列のパフォーマンスの向上は良いことでした。今、収集されない文字列に格納された巨大なXMLドキュメントでは、これが問題になります...したがって、Stringサブストリングで同じ基本配列を使用しないように変更し、より大きな文字配列をより迅速に収集できるようにします。

スタックを乱用しないでください

一つは、可能性があり、文字列の値の周りの代わりに可変性の問題を回避するために、不変の文字列への参照を渡します。ただし、大きな文字列の場合、これをスタックに渡すと...システムに悪用されます(xmlドキュメント全体を文字列としてスタックに置き、それらを取り出したり、引き渡し続けたりします)。

重複排除の可能性

確かに、これはStringsが不変である理由の最初の動機ではありませんでしたが、不変のStringsが良い理由である理由を検討するとき、これは確かに考慮すべきものです。

文字列を少し使ったことがある人なら誰でも、メモリを吸い取ることができることを知っています。これは、しばらく使用されているデータベースからデータをプルするようなことをしている場合に特に当てはまります。多くの場合、これらの刺し傷は、何度も何度も同じ文字列です(各行に1回)。

現在、多くの大規模なJavaアプリケーションがメモリのボトルネックになっています。これらのタイプのアプリケーションのJavaヒープライブデータセットの約25%がStringオブジェクトによって消費されることが測定により示されています。さらに、これらのStringオブジェクトの約半分は重複しています。重複とは、string1.equals(string2)がtrueであることを意味します。ヒープ上で文字列オブジェクトを複製することは、本質的にメモリの無駄です。...

Java 8 update 20では、これに対処するためにJEP 192(上記引用の動機)が実装されています。文字列の重複排除がどのように機能するかの詳細に入ることなく、文字列自体が不変であることが不可欠です。StringBuilderは重複する可能性があり、誰かがあなたの下から何かを変更したくないので、重複排除できません。不変の文字列(その文字列プールに関連する)は、通過できることを意味し、同じ文字列が2つ見つかった場合、一方の文字列参照を他方にポイントし、ガベージコレクターに新しく使用されていない文字列を消費させることができます。

他の言語

Objective C(Javaより前のバージョン)にはとがNSStringありNSMutableStringます。

C#と.NETは、デフォルトの文字列が不変であるという同じ設計選択を行いました。

Lua文字列も不変です。

Pythonも。

歴史的に、Lisp、Scheme、Smalltalkはすべて文字列インターンしているため、不変です。より現代的な動的言語では、文字列を不変にする必要がある何らかの方法で文字列を使用することがよくあります(Stringではない場合がありますが、不変です)。

結論

これらの設計上の考慮事項は、多数の言語で何度も何度も行われています。不変な文字列は、その不器用さのすべてについて、代替よりも優れており、コードの改善(バグの削減)および実行可能ファイル全体の高速化につながるというのが一般的なコンセンサスです。


3
Javaは、可変および不変の文字列を提供します。この回答では、不変の文字列で実行できるパフォーマンス上の利点と、不変のデータを選択する理由について説明します。ただし、不変バージョンがデフォルトバージョンである理由については説明しません。
ビリーONeal

3
@BillyONeal:安全なデフォルトと安全でない代替は、ほとんどの場合、反対のアプローチよりも安全なシステムにつながります。
ヨアヒムザウアー

4
@BillyONeal不変がデフォルトでない場合、並行性、セキュリティ、およびハッシュの問題がより一般的になります。言語設計者は、プログラマーの効率を向上させるために多くの一般的なバグを防ぐためにデフォルトが設定されている言語を作成することを(一部Cに対応して)選択しました(これらのバグをもう心配する必要はありません)。不変の文字列では、変更可能な文字列よりもバグが少なくなります(明らかで隠れています)。

@Joachim:私はそうではないと主張していません。
ビリーONeal

1
技術的には、Common Lispには、「文字列のような」操作のための可変文字列と、不変の識別子の不変の名前を持つシンボルがあります。
Vatine

21

思い出せる理由:

  1. 文字列プールの場合、1つの文字列オブジェクト/リテラル​​(たとえば「XYZ」)が多くの参照変数によって参照されるため、文字列を不変にしない文字列プール機能はまったく使用できません。 。

  2. 文字列は、ネットワーク接続を開く、データベース接続を開く、ファイルを開くなど、多くのJavaクラスのパラメーターとして広く使用されています。Stringが不変でない場合、深刻なセキュリティ上の脅威になります。

  3. 不変性により、Stringはハッシュコードをキャッシュできます。

  4. スレッドセーフにします。


7

1)ストリングプール

Javaデザイナーは、Stringがあらゆる種類のJavaアプリケーションで最もよく使用されるデータ型になることを知っているため、最初から最適化することを望んでいました。その方向の重要なステップの1つは、文字列リテラルを文字列プールに格納するというアイデアでした。目標は、一時的な文字列オブジェクトを共有することで減らすことであり、共有するには不変クラスからのものでなければなりません。相互に不明な2つのパーティと可変オブジェクトを共有することはできません。2つの参照変数が同じStringオブジェクトを指している架空の例を見てみましょう。

String s1 = "Java";
String s2 = "Java";

s1がオブジェクトを「Java」から「C ++」に変更した場合、参照変数も値s2 = "C ++"を取得しますが、それについては知りません。Stringを不変にすることにより、このStringリテラルの共有が可能になりました。つまり、JavaでStringをfinalまたはImmutableにしないと、Stringプールの重要なアイデアを実装できません。

2)セキュリティ

Javaは、すべてのレベルのサービスで安全な環境を提供するという点で明確な目標を持っています。Stringは、これらのセキュリティ全体で重要です。文字列は、多くのJavaクラスのパラメータとして広く使用されています。たとえば、ネットワーク接続を開くために、ホストとポートを文字列として渡すことができます。データベースURLを文字列として渡します。Stringが不変ではない場合、ユーザーはシステム内の特定のファイルへのアクセスを許可した可能性がありますが、認証後にPATHを別のものに変更できるため、深刻なセキュリティ問題が発生する可能性があります。同様に、データベースまたはネットワーク内の他のマシンに接続しているときに、文字列値を変更するとセキュリティ上の脅威になる可能性があります。可変文字列は、Reflectionでもセキュリティ問題を引き起こす可能性がありますが、

3)クラスローディングメカニズムでの文字列の使用

StringをfinalまたはImmutableにしたもう1つの理由は、クラスローディングメカニズムで頻繁に使用されていたという事実によるものです。Stringは不変ではないため、攻撃者はこの事実を利用して、java.io.Readerなどの標準Javaクラスのロード要求を悪意のあるクラスcom.unknown.DataStolenReaderに変更できます。Stringをfinalかつ不変に保つことにより、少なくともJVMが正しいクラスをロードしていることを確認できます。

4)マルチスレッドの利点

並行性とマルチスレッドはJavaの主要な製品であるため、Stringオブジェクトのスレッドセーフについて考えることは非常に理にかなっています。Stringが広く使用されることが予想されていたため、Immutableにすることは外部同期を行わないことを意味し、複数のスレッド間でStringを共有するコードがよりクリーンになります。この単一の機能により、すでに複雑で混乱しやすく、エラーが発生しやすい同時実行コーディングがはるかに簡単になります。Stringは不変であり、スレッド間で共有するだけなので、コードは読みやすくなります。

5)最適化とパフォーマンス

これで、クラスを不変にすると、作成したクラスは変更されないことが事前にわかります。これにより、キャッシングなどの多くのパフォーマンス最適化のオープンパスが保証されます。文字列自体は、私が変更するつもりはないことを知っているので、文字列はハッシュコードをキャッシュします。さらに、ハッシュコードを遅延計算し、一度作成したら、キャッシュするだけです。単純な世界では、StringオブジェクトのhashCode()メソッドを最初に呼び出すと、ハッシュコードが計算され、その後のhashCode()の呼び出しはすべて、計算済みのキャッシュされた値を返します。これにより、HashtableやHashMapなどのハッシュベースのマップでStringが頻繁に使用されるため、パフォーマンスが向上します。ハッシュコードのキャッシングは、文字列自体の内容に依存するため、不変で最終的なものにするまで不可能でした。


5

Java Virtual Machineは、文字列操作に関していくつかの最適化を実行しますが、それ以外の方法では実行できませんでした。たとえば、値が「Mississippi」の文字列があり、「Mississippi」.substring(0、4)を別の文字列に割り当てた場合、知っている限り、「Miss」を作成する最初の4文字のコピーが作成されました。 。どちらも同じ元の文字列「ミシシッピ」を共有し、一方が所有者であり、もう一方が位置0から4までのその文字列の参照であることがわかりません(所有者への参照は、所有者が範囲外になったときのガベージコレクター)

これは、「ミシシッピ」ほど小さい文字列では簡単ですが、より大きな文字列と複数の操作がある場合、文字列をコピーする必要がないため、時間を大幅に節約できます。文字列が可変の場合、元の文字列を変更するとサブ文字列「コピー」にも影響するため、これを行うことはできません。

また、ドナルが言及しているように、利点はその欠点によって大きく評価されます。ライブラリに依存するプログラムを作成し、文字列を返す関数を使用するとします。その値が一定のままであることをどのようにして確認できますか?そのようなことが起こらないようにするには、常にコピーを作成する必要があります。

同じ文字列を共有する2つのスレッドがある場合はどうなりますか?現在別のスレッドによって書き換えられている文字列を読み取りたくないでしょうか?そのため、文字列はスレッドセーフである必要があります。スレッドセーフは一般的なクラスであり、事実上すべてのJavaプログラムを非常に遅くします。それ以外の場合、その文字列を必要とするすべてのスレッドのコピーを作成するか、その文字列を使用するコードを同期ブロックに配置する必要がありますが、どちらもプログラムの速度を低下させるだけです。

これらすべての理由から、それはC ++との差別化を図るためにJavaに対して行われた初期の決定の1つでした。


理論的には、コピーオンミューテーション-if-sharedを可能にするマルチレイヤーバッファ管理を行うことができますが、マルチスレッド環境で効率的に作業することは非常に困難です。
ドナルドフェローズ

@DonalFellows Java仮想マシンはJavaで書かれていないので(明らかに)、共有ポインタなどを使用して内部的に管理されていると思いました。
ニール

5

文字列が不変である理由は、言語の他のプリミティブ型との一貫性にあります。あなたがいる場合はint値42を含む、あなたがそれに値1を追加し、あなたは、開始値とは全く無関係である新しい値、43を得る42を変更しないでください。文字列以外のプリミティブを変更しても、概念的な意味はありません。また、文字列を不変として扱うプログラムは、多くの場合、推論や理解が容易です。

さらに、あなたが見るように、Javaは実際に可変文字列と不変文字列の両方を提供しStringBuilderます。実際、デフォルトのみが不変の文字列です。StringBuilderあらゆる場所に参照を渡したい場合は、完全に歓迎します。Javaは、型システムでの可変性またはその欠如を表現するためのサポートがないため、これらの概念に別個の型(StringおよびStringBuilder)を使用します。型システムで不変性をサポートしている言語(C ++などconst)では、多くの場合、両方の目的に役立つ単一の文字列型があります。

はい、文字列を不変にすると、インターンなどの不変文字列に固有の最適化を実装でき、スレッド間で同期せずに文字列参照を渡すことができます。ただし、これにより、メカニズムが、言語の意図された目標と単純で一貫性のある型システムと混同されます。誰もがガベージコレクションを間違った方法で考える方法にこれを例えます。ガベージコレクションは「未使用のメモリの再生」ではありません。「無制限のメモリを搭載したコンピューターのシミュレーション」です。説明されているパフォーマンスの最適化は、不変文字列の目標を実際のマシンで適切に実行するために行われます。そのような文字列がそもそも不変である理由ではありません。


@ Billy-Oneal ..「値42を含むintがあり、値1を追加した場合、42は変更しません。新しい値43を取得します。これは開始とはまったく関係ありません値。」確かですか?
シャミットヴァーマ14

@シャミット:はい、確かです。43に1〜42の結果を追加すると、それは、数42は数43と同じものを意味することはありません
ビリーONeal

@Shamit:同様に、あなたのような何かを行うことはできません43 = 6し、数43は数6と同じものを意味することを期待
ビリーONeal

int i = 42; i = i + 1; メモリ内の42を保存し、その後43に同じ場所に値を変更します。このコードは、それで実際には、変数「i」は43の新しい値を取得
Shamitバーマ

@Shamit:その場合、i42ではなく突然変異しましたstring s = "Hello "; s += "World";。考慮してください。variableの値を変更しましたs。しかし、文字列"Hello ""World"および"Hello World"不変です。
ビリーONeal 14

4

不変性とは、所有していないクラスが保持する定数を変更できないことを意味します。所有していないクラスにはJavaの実装のコアにあるクラスが含まれ、変更すべきでない文字列にはセキュリティトークン、サービスアドレスなどが含まれます。これらの種類を実際に変更することはできません物事(およびこれは、サンドボックスモードで動作するときに二重に適用されます)。

Stringが不変ではない場合、文字列の内容をその下で変更したくないコンテキストから取得するたびに、「念のため」コピーを取得する必要があります。それは非常に高価になります。


4
このまったく同じ引数は、だけでなく、すべての型に適用されますString。しかし、たとえば、Arraysはそれでも変更可能です。それで、なぜString不変であり、そうではArrayないのか。そして、不変性が非常に重要な場合、Javaが不変オブジェクトの作成と操作をそれほど難しくするのはなぜですか?
ヨルグWミットタグ

1
@JörgWMittag:基本的に彼らがどのように過激になりたかったのかという質問だと思います。Java 1.0の時代には、不変のStringを持つことはかなり過激でした。(主に、または排他的に)不変のコレクションフレームワークも同様に、言語を広く使用するには過激すぎる可能性があります。
ヨアヒムザウアー

効果的な不変のコレクションフレームワークを実行するのは、パフォーマンスを上げるのに非常に注意が必要です。そのようなことを書いた人(Javaではない)として話してください。また、不変配列があることを完全に望みます。それは私にかなりの仕事を救ったでしょう。
ドナルドフェローズ

@DonalFellows:pcollectionsはそれを行うことを目指しています(ただし、自分で使用したことはありません)。
ヨアヒムザウアー

3
@JörgWMittag:すべての型は不変であるべきだと主張する人(一般的に純粋に機能的な観点から)がいます。同様に、並列ソフトウェアと並行ソフトウェアで可変状態を操作する場合に扱うすべての問題を合計すると、不変オブジェクトの操作は可変オブジェクトよりもはるかに簡単であることに同意するかもしれません。
スティーブンエバーズ

2

一部のデータを受け入れ、その正当性を検証してから渡す(DBに保存するなど)システムを想像してください。

データがaでStringあり、少なくとも5文字の長さが必要であると仮定します。メソッドは次のようになります。

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

storeInDatabaseここでが呼び出されるとinput、要件に適合することに同意できます。しかしString可変であれば、呼び出し側は、検証された直後にデータベースに保存される前にinputオブジェクトを(別のスレッドから)変更できます。これには適切なタイミングが必要であり、おそらく毎回うまくいくとは限りませんが、時折、彼はデータベースに無効な値を保存することができます。

不変データ型は、この(および関連する多くの)問題に対する非常に単純な解決策です。値をチェックするときはいつでも、チェックされた条件が後で真であるという事実に依存することができます


説明ありがとう。このようなハンドルメソッドを呼び出すとどうなりますか。ハンドル(新しい文字列(入力+ "naberlan"))。私はこのようにdbに無効な値を保存できると思います。
yfklon

1
@blank:以降も、inputhandle方法は(どんなすでに長すぎるん元で inputある)、それは単に例外をスローしていました。 メソッドを呼び出す前に新しい入力を作成しています。それは問題ではありません。
ヨアヒムザウアー

0

一般的に、値型参照型に遭遇します。値型では、それを表すオブジェクトを気にせず、値を気にします。値を指定した場合、その値は同じままであると予想されます。突然変化することは望ましくありません。数字の5は値です。突然6に変わるとは思わないでしょう。文字列「Hello」は値です。突然「P *** off」に変わるとは思わないでしょう。

参照型は、オブジェクトを気に、あなたはそれを変更することが予想されます。たとえば、多くの場合、配列の変更が予想されます。私があなたに配列を与え、そしてあなたがそれを正確にそのままに保ちたいなら、あなたはそれを変更しないことを私に信頼しなければならないか、あなたはそれのコピーを作る。

Java文字列クラスでは、設計者は決定を行う必要がありました。文字列が値型のように振る舞うのか、参照型のように振る舞うのが良いのでしょうか。Java文字列の場合、それらは値型であることが決定されました。つまり、オブジェクトであるため、不変オブジェクトでなければなりません。

反対の決定を下すこともできたかもしれませんが、私の意見では、多くの頭痛の種になりました。他の場所で述べたように、多くの言語が同じ決定を下し、同じ結論に達しました。例外はC ++で、1つの文字列クラスがあり、文字列は定数または非定数にできますが、C ++では、Javaとは異なり、オブジェクトパラメータは参照としてではなく値として渡すことができます。


0

誰もこれを指摘していないことに本当に驚きました。

回答:たとえそれが可変であったとしても、それはあなたにとって大きな利益にはなりません。追加のトラブルを引き起こす限り、それはあなたに利益をもたらさないでしょう。突然変異の最も一般的な2つのケースを調べてみましょう。

文字列の1文字を変更する

Java文字列の各文字は2バイトまたは4バイトのいずれかを使用するため、自問してください。既存のコピーを変更できる場合、何か得られますか?

2バイト文字を4バイト文字(またはその逆)に置き換えるシナリオでは、文字列の残りの部分を2バイト左または右にシフトする必要があります。これは、計算の観点から文字列全体を完全にコピーすることと同じです。

これはまた、通常は望ましくない本当に不規則な動作です。誰かが英語のテキストを使用してアプリケーションをテストし、そのアプリケーションが中国などの外国に採用されると、全体が奇妙に動作し始めると想像してください。

既存の文字列に別の文字列(または文字)を追加する

2つの任意の文字列がある場合、それらは2つの異なるメモリ位置に配置されます。2番目の文字列を追加して最初の文字列を変更する場合、最初の文字列が既に使用されている可能性があるため、最初の文字列の最後に追加メモリを要求することはできません。

連結された文字列をまったく新しい場所にコピーする必要があります。これは、両方の文字列が不変である場合とまったく同じです。

効率的に追加を行いたい場合StringBuilderは、将来の追加のために、文字列の末尾にかなりの量のスペースを予約するを使用できます。


-2
  1. それらは高価であり、不変に保つことで、メイン文字列のバイト配列を共有するサブ文字列などが可能になります。(新しいバイト配列を作成してコピーする必要がないので、速度が向上します)

  2. セキュリティ-パッケージまたはクラスコードの名前を変更したくない

    [StringBuilder srcで見た古い3を削除-文字列とメモリを共有しません(変更されるまで)1.3または1.4だったと思います]

  3. キャッシュハッシュコード

  4. 複数の文字列にはSB(必要に応じてビルダーまたはバッファー)を使用します


2
1.もちろん、これが発生すると、ストリングの大部分を破壊できないというペナルティがあります。インターンは無料ではありません。ただし、多くの実際のプログラムのパフォーマンスは向上します。2.その要件を満たすことができる「string」と「ImmutableString」が簡単に存在する可能性があります。3.私は、私はそれを理解していない...
ビリーONeal

.3。ハッシュコードをキャッシュする必要がありました。これも変更可能な文字列で実行できます。@ billy-oneal
tgkprog

-4

文字列はJavaのプリミティブデータ型である必要がありました。もしそうであれば、文字列はデフォルトで可変になり、最終キーワードは不変文字列を生成します。可変文字列は便利なので、stringbuffer、stringbuilder、charsequenceクラスの可変文字列には複数のハックがあります。


3
これは、質問が質問するようになった現在の「なぜ」側面に答えません。さらに、java finalはそのようには機能しません。可変文字列はハックではなく、文字列の最も一般的な使用法とjvmを改善するために実行できる最適化に基づく実際の設計上の考慮事項です。

1
「なぜ」に対する答えは、言語設計の決定が下手です。可変文字列をサポートするためのわずかに異なる3つの方法は、コンパイラ/ JVMが処理すべきハックです。
CWallach

3
StringとStringBufferがオリジナルでした。StringBuilderは、StringBufferの設計の難しさを認識して後で追加されました。さまざまなオブジェクトである可変および不変の文字列は、多くの言語で設計上の考慮事項が何度も行われ、それぞれが異なるオブジェクトであると判断されるたびに見つかりました。C#「文字列は不変.NET文字列が不変なのはなぜですか?、目的のC NSStringは不変ですが、NSMutableStringは可変です。stackoverflow.com/questions/9544182
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.