4

非常によく似た質問が尋ねられ、すでに回答されていることを知っています。私が見つけることができたものを読みましたが、まだ 100% 明確ではありません。

このコード スニペットを考慮すると、次のようになります。

public static void fooMethod {

   while(<...>) {
     ....
     final int temp = <something>;
     ....
   }
}

内部クラスはなく、特別なものや珍しいものは何もありません。私には直感に反するようです。

上記のサンプルでローカル変数finalを宣言することは、何らかの目的に役立ちますか?

finalhere の有無にかかわらず、コンパイラがまったく同じバイトコードを生成することを正しく理解していますか?

ここで何か不足していますか?RTFM の場合は、正しい方向に向けてください。

フォローアップの質問(可能であれば)

このように書き直すことで、何を得たり失ったりするのtempでしょうか (プリミティブである必要はないという理解で)。

public static void fooMethod2 {

   int temp;
   while(<...>) {
     ....
     temp = <something>;
     ....
   }
}
4

3 に答える 3

9

簡単に言うと、ローカル変数とパラメーターでキーワードを使用するfinalと、生成されたバイトコード (ファイル) に到達せず、予想どおり、その使用は実行時に影響しません。(コンパイル時に、違いが生じる可能性がありますが、以下を確認してください。).class

これらの場合、匿名の内部クラスのために強制されない場合、それは単なるスタイルの選択であり、変数の意図したスコープを文書化するのに役立ちます。

以下のテストは、その情報を確認します。



1: コンパイラが何かを作ることができる場合、使用finalすることで違いが生まれます:

このスニペットを見てください:

boolean zZ = true;
while (zZ) {
    int xX = 1001;         // <------------- xX
    int yY = 1002;         // <------------- yY
    zZ = (xX == yY);
}

2 つのint変数、xXおよびyY. 最初は両方を asfinalと宣言し、2 回目は両方finalから を削除しました。生成されたバイトコードは次のとおりです (出力はjavap -c):

両方final:

     0: iconst_1             // pushes int 1 (true) onto the stack
     1: istore_1             // stores the int on top of the stack into var zZ
     2: goto          15
     5: sipush        1001   // pushes 1001 onto the operand stack
     8: istore_2             // stores on xX
     9: sipush        1002   // pushes 1002 onto the operand stack
    12: istore_3             // stores on yY
    13: iconst_0             // pushes 0 (false): does not compare!! <---------
    14: istore_1             // stores on zZ
    15: iload_1              // loads zZ
    16: ifne          5      // goes to 5 if top int (zZ) is not 0
    19: return        

両方非final:

    // 0: to 12: all the same
    13: iload_2              // pushes xX onto the stack
    14: iload_3              // pushes yY onto the stack
    15: if_icmpne     22     // here it compares xX and yY! <------------
    18: iconst_1      
    19: goto          23
    22: iconst_0      
    23: istore_1      
    24: iload_1       
    25: ifne          5
    28: return        

上記の場合、それらがfinalである場合、コンパイラはそれらが等しくないことを認識し、それらを比較することはありません(falseバイトコード内のどこにでも生成されxX == yYます)。

このことから、バイトコードに関しては、コンパイラfinal. (意味があると言っているわけではありませんが、ここでのスタイルの選択finalだけではないことは確かです。)


2: コンパイラが何も結論付けられない場合、finalローカル変数を使用することは単なる設計上の選択です。

次に、次のコードを使用します。

boolean zZ = true;
int aA = 1001;
int bB = 1002;
while (zZ) {
    final int xX = aA;   // <------- took away the "final" here, didnt matter
    final int yY = bB;   // <------- took away the "final" here, didnt matter
    zZ = (xX == yY);
}

この場合、 を使用しても、コンパイラはとが等しいfinalかどうかをコンパイラ時間に伝えることができませんよね?xXyY

このため、生成されたバイトコードは、 を使用してクラスを生成しても使用せずに生成しても、まったく同じ(同じ MD5!) であることがわかります。final

一般的なケースでは、 を使用するとパフォーマンス上の利点があると言う人もいれば反対する人もいますが、ローカル ブロックで を使用することは間違いなくfinalスタイルfinal選択にすぎませ


3: ループ内またはループ外のローカル変数 - 違いはまったくありません:

このスニペット用に生成されたバイトコード...

boolean zZ = true;
int aA = 1001, bB = 1002;
while (zZ) {
    int xX = aA;                      // <--- declaration is inside WHILE
    int yY = bB;
    zZ = (xX == yY);
}

...そして、このスニペット用に生成されたバイトコード...

boolean zZ = true;
int aA = 1001, bB = 1002;
int xX, yY;                           // <--- declaration is outside WHILE
while (zZ) {
    xX = aA;
    yY = bB;
    zZ = (xX == yY);
}

...まったく同じです(もちろん、行番号だけが変更されました)。

(プリミティブ型の変数だけでなく) オブジェクトを使用した他のテストでも、同じ動作が示されました。

他の場所で使用されていない場合、ループ内またはループ外でローカル変数を宣言することは、バイトコードの影響がなく、ほとんど設計上の選択である結論付けても安全です。

注:すべてのテストは、Oracle の JRE、バージョン 1.7.0_13 で行われました。

于 2013-05-26T02:14:33.810 に答える
4

final定数変数のキーワードです。final を宣言すると、後でループ内で再割り当てできなくなります。

temp最終かどうかに関係なく、反復ごとに再宣言されます。

例えば:

while (...)
{
    final int temp = ...;

    temp = 5; // compiler error
}

しかし、一定でない場合(最終):

while (...)
{
    int temp = ...;

    temp = 5; // fine
}
于 2013-05-26T01:59:41.933 に答える
1

これをまったく別の観点から考えてみましょう。関数型プログラミング言語では、ほとんどすべての代入が final であり、クラスが不変であるのが通常のケースです。つまり、非最終割り当ておよび/または変更可能なクラスは例外です。

コードが Scala で記述されている場合、IntelliJ IDE は「この割り当てを最終的に変更できる」というヒントを表示します。

後でコードを読むと、この割り当てがさらに下の行を変更しないことが一目でわかるので、「ファイナル」に本当に感謝しています。インスタンスが不変であることがわかっている場合は、これも役立ちます。

さらに、一貫して「最終」を使用すると、最終ではない変数が可視化され、これらの変数は通常、観察する最も重要なものになります。

于 2013-05-26T07:24:17.137 に答える