Java言語ドキュメントには次のように書かれています:
プリミティブ型または文字列が定数として定義されていて、その値がコンパイル時にわかっている場合、コンパイラはコード内のすべての定数名をその値に置き換えます。これはコンパイル時定数と呼ばれます。
私の理解は、コードがあるかどうかです:
private final int x = 10;
次に、コンパイラーは、コード内のすべてのx
をリテラル10
に置き換えます。
しかし、定数が実行時に初期化されると仮定します。
private final int x = getX(); // here getX() returns an integer value at run-time.
コンパイル時の定数と比較して、パフォーマンスの低下はありますか(無視できるかもしれません)。
別の質問は、以下のコード行かどうかです:
private int y = 10; // here y is not final
コンパイラーはコンパイル時定数と同じように扱われますか?
最後に、答えから私が理解することは:
final static
はコンパイル時の定数を意味しますfinal
は定数であることを意味しますが、実行時に初期化されますstatic
は実行時に初期化されることを意味しますfinal
なしは変数であり、定数として扱われません。私の理解は正しいですか?
コンパイル時定数は以下でなければなりません:
したがって、private final int x = getX();
は定数ではありません。
2番目の質問private int y = 10;
は定数ではない(この場合は最終ではない)ため、オプティマイザーは値が将来変更されないことを確認できません。したがって、定数値ほど最適化することはできません。答えは次のとおりです。いいえ、コンパイル時定数と同じようには扱われません。
[〜#〜] jls [〜#〜] は、final
変数と定数を次のように区別します。
final
変数変数は
final
と宣言できます。final
変数は、一度だけ割り当てることができます。final
変数が割り当ての直前に完全に割り当て解除されない限り、コンパイル時エラーです( §16(Definite Assignment) )。
final
変数が割り当てられると、常に同じ値が含まれます。final
変数がオブジェクトへの参照を保持している場合、オブジェクトの状態はオブジェクトの操作によって変更される可能性がありますが、変数は常に同じオブジェクトを参照します。配列はオブジェクトなので、これは配列にも当てはまります。final
変数が配列への参照を保持している場合、配列のコンポーネントは配列の操作によって変更される可能性がありますが、変数は常に同じ配列を参照します。空白
final
は、宣言に初期化子がないfinal
変数です。
定数変数は、プリミティブ型または型
final
のString
変数であり、定数式(- §15.28 )。
この定義から、定数は次のようでなければならないことがわかります。
final
String
final
ではない))[〜#〜] jls [〜#〜] にはコンパイル時定数という句が含まれていません。ただし、プログラマーはコンパイル時定数とconstantを同じ意味で使用することがよくあります。
final
変数が定数と見なされる上で概説された基準を満たさない場合、技術的にはfinal
変数と呼ばれるべきです。
JLSによれば、「定数変数」が静的である必要はありません。
したがって、「定数変数」は静的または非静的(インスタンス変数)の可能性があります。
ただし、JLSは、変数が「定数変数」であること(他に単なる最終的なものである必要はありません)について、他のいくつかの要件を課しています。
定数変数は、定数式で初期化されるプリミティブ型またはString型の最終的な変数です(§15.28 )。
コンパイル時の定数式は、プリミティブ型の値または文字列が突然完了せず、以下のみを使用して構成される式を表す式です。
プリミティブ型のリテラルと文字列型のリテラル(§3.10.1、§3.10.2、§3.10.3、§3.10.4、§3.10.5)
プリミティブ型へのキャストと文字列型へのキャスト(§15.16)
単項演算子+、-、〜、および! (++や-は除く)(§15.15.3、§15.15.4、§15.15.5、§15.15.6)
乗法演算子*、/、および%(§15.17)
加算演算子+および-(§15.18)
シフト演算子<<、>>、および>>>(§15.19)
関係演算子<、<=、>、および> =(instanceofは除く)(§15.20)
等価演算子==および!=(§15.21)
ビットごとの論理演算子&、^、および| (§15.22)
条件付きAND演算子&&および条件付きOR演算子|| (§15.23、§15.24)
三項条件演算子? :(§15.25)
含まれている式が定数式である括弧で囲まれた式(§15.8.5)。
定数変数(4.14.12.4)を参照する単純な名前(6.5.6.1)。
TypeName形式の修飾名(§6.5.6.2)。定数変数を参照する識別子(§4.12.4)。
final
キーワードは、変数が一度だけ初期化されることを意味します。実際の定数もstatic
として宣言する必要があります。そのため、コンパイラによって定数として扱われる例はありません。それにもかかわらず、最後のキーワードは、(およびコンパイラーに)変数が(コンストラクター内で、または文字通り)1度だけ初期化されることを通知します。コンパイル時にそれらの値を割り当てる必要がある場合、フィールドは静的でなければなりません。
パフォーマンスはそれほど影響を受けませんが、プリミティブ型は不変であることを念頭に置いてください。プリミティブ型を作成すると、ガベージコレクターが削除するまでメモリにその値が保持されます。したがって、変数y = 1;
があり、それをメモリ内でy = 2;
に変更した場合、JVMは両方の値を持ちますが、変数は後者を「ポイント」します。
private int y = 10; //ここではyは最終ではありません
コンパイラーはコンパイル時定数と同じように扱われますか?
いいえ。これは、実行時に使用され、作成、初期化されたインスタンス変数です。
あります可能性があります非常に小さいパフォーマンスが低下一部private final int x = getX();
のマシンには少なくとも1つのメソッド呼び出しが含まれるため(これはコンパイル時の定数ではないという事実に加えて)しかし、あなたが言ったように、それはnegligibleになるので、なぜわざわざ?
2番目の質問については、y
は最終ではなく、実行時に変更される可能性があるため、コンパイル時定数ではありません。
private final int x = getX();
オブジェクトが初めて宣言されたときに呼び出されます。パフォーマンスの「低下」はgetX()
に依存しますが、それはボトルネックを作成するようなものではありません。
単純に言えば、コンパイラは、リファレンスパラメータを使用する代わりに、リファレンスを指定された実際の値に置き換えます。
public static void main(String[] args) {
final int x = 5;
}
すなわち。コンパイル中に、コンパイラは参照変数「x」を使用するよりも、コンパイルのために直接5の初期値をとります。