文字列リテラル「foo」の複数の使用を含むクラスに出くわしました。
私が知りたいのは、文字列をfinalとして宣言し、すべてのリテラルをfinal変数に置き換える代わりに、このアプローチを使用することの利点と影響(オブジェクトの作成、メモリ使用量、速度の点で)は何ですか?
たとえば(明らかに実際のWordの使用法ではありませんが):
private static final String FINAL_STRING = "foo";
public void stringPrinter(){
for(int i=0;i<10;i++){
System.out.println(FINAL_STRING);
}
}
対:
public void stringPrinter(){
for(int i=0;i<10;i++){
System.out.println("foo");
}
}
どちらが望ましいのか、そしてその理由(文字列値が一定のままであると仮定)?
上記(2番目)の例では、10個のStringオブジェクトが作成されますか、それともJVMは、実際には1つのリテラルのみが使用されていることを認識し、1つの参照を作成します。もしそうなら、(最初の例のように)文字列をfinalとして宣言することに利点はありますか?
インタープリターされたコードが文字列リテラルを単一の参照に置き換えた場合でも、同じリテラルが複数の場所で発生した場合でも適用されます。
public void stringPrinter(){
for(int i=0;i<5;i++){
System.out.println("foo"); // first occurence
System.out.println("foo"); // second occurence
}
}
それらはまったく同じになります。どちらの場合も、リテラルはインターンされ(その文字列が他のすべての定数/リテラルと同じインスタンスを共有するコンパイル時定数式)、スマートコンパイラ+ランタイムは両方を最適化された例に減らすのに問題はありません。
利点は、保守性にあります。リテラルを変更する場合は、定数を使用して1つのオカレンスを変更するだけで済みますが、インラインに含まれている場合は、すべてのインスタンスを検索して変更する必要があります。
JLSから
String型のコンパイル時定数は、メソッドString.intern。を使用して、一意のインスタンスを共有するために常に「インターン」されます。
つまり、いいえ、文字列オブジェクトは1つだけになります。
マークが指摘しているように、これは厳密には保守性の問題であり、パフォーマンスの問題ではありません。
利点はパフォーマンスではなく、保守性と信頼性にあります。
つい最近出会った実例を見てみましょう。プログラマーは、トランザクションのタイプを識別するStringパラメーターを受け取る関数を作成しました。次に、プログラムで、彼はこのタイプに対して文字列比較を行いました。お気に入り:
if (type.equals("stock"))
{ ... do whatever ... }
次に、この関数を呼び出して、値「Stock」を渡しました。
大文字の違いに気づきましたか?元のプログラマーもそうではありませんでした。両方のリストを見ても、大文字の違いに気付かなかったので、理解するのはかなり微妙なバグであることがわかりました。
代わりに彼が最終的な静的を宣言した場合、
final static String stock="stock";
その後、初めて「stock」ではなく「Stock」を渡そうとしたときに、コンパイル時エラーが発生していました。
この例では、列挙型を作成する方がよいでしょうが、実際に文字列を出力ファイルなどに書き込む必要があったため、文字列である必要があると仮定します。
最終的な静力学を使用すると、少なくともxの利点があります。
(1)スペルを間違えると、微妙な実行時エラーではなく、コンパイル時エラーが発生します。
(2)スタティックは、意味のある名前を値に割り当てることができます。どちらがより理解しやすいですか:
if (employeeType.equals("R")) ...
または
if (employeeType.equals(EmployeeType.RETIRED)) ...
(3)関連する値が複数ある場合は、最終的な統計のグループをプログラムの先頭にまとめて、将来の読者にすべての可能な値を通知することができます。関数が値を2つまたは3つのリテラルと比較するのを見たことが何度もあります。そしてそれは私に疑問を残します:他の可能な値はありますか、それともこれですか? (列挙型を使用する方がよい場合もありますが、それは別の話です。)
すべての文字列リテラルは文字列キャッシュに保持されます(これはすべてのクラスにまたがります)
定数を使用すると、コードがより明確になり、文字列にコンテキストが与えられ、同じ文字列が複数の場所に表示される場合にコードがespを維持しやすくなります。
これらの文字列リテラルは内部化されているため、ループ内に新しいStringオブジェクトは作成されません。ただし、同じリテラルを2回使用することは、コードの臭いの兆候である可能性があります。ただし、速度やメモリ使用量に関してはそうではありません。
あなたが提供している場合、それをどこかでFINAL_STRING
として宣言する最大の理由は、それが一元化された場所にとどまるようにするためだと思います。その文字列定数のインスタンスは1つだけですが、最初の例は保守がはるかに簡単です。