web-dev-qa-db-ja.com

コンパイル時の定数と変数

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

コンパイラーはコンパイル時定数と同じように扱われますか?


最後に、答えから私が理解することは:

  1. final staticはコンパイル時の定数を意味します
  2. finalは定数であることを意味しますが、実行時に初期化されます
  3. ただstaticは実行時に初期化されることを意味します
  4. finalなしは変数であり、定数として扱われません。

私の理解は正しいですか?

38
NINCOMPOOP

コンパイル時定数は以下でなければなりません:

  • 最終宣言
  • プリミティブまたは文字列
  • 宣言内で初期化
  • 定数式で初期化

したがって、private final int x = getX();は定数ではありません。

2番目の質問private int y = 10;は定数ではない(この場合は最終ではない)ため、オプティマイザーは値が将来変更されないことを確認できません。したがって、定数値ほど最適化することはできません。答えは次のとおりです。いいえ、コンパイル時定数と同じようには扱われません。

56
msi

[〜#〜] jls [〜#〜] は、final変数と定数を次のように区別します。

final変数

変数はfinalと宣言できます。 final変数は、一度だけ割り当てることができます。 final変数が割り当ての直前に完全に割り当て解除されない限り、コンパイル時エラーです( §16(Definite Assignment) )。

final変数が割り当てられると、常に同じ値が含まれます。 final変数がオブジェクトへの参照を保持している場合、オブジェクトの状態はオブジェクトの操作によって変更される可能性がありますが、変数は常に同じオブジェクトを参照します。配列はオブジェクトなので、これは配列にも当てはまります。 final変数が配列への参照を保持している場合、配列のコンポーネントは配列の操作によって変更される可能性がありますが、変数は常に同じ配列を参照します。

空白finalは、宣言に初期化子がないfinal変数です。

定数

定数変数は、プリミティブ型または型finalString変数であり、定数式(- §15.28 )。

この定義から、定数は次のようでなければならないことがわかります。

  • 宣言済みfinal
  • プリミティブ型または型String
  • 宣言内で初期化された(空白finalではない))
  • 定数式 で初期化

コンパイル時定数はどうですか?

[〜#〜] jls [〜#〜] にはコンパイル時定数という句が含まれていません。ただし、プログラマーはコンパイル時定数constantを同じ意味で使用することがよくあります。

final変数が定数と見なされる上で概説された基準を満たさない場合、技術的にはfinal変数と呼ばれるべきです。

4
user9514304

JLSによれば、「定数変数」が静的である必要はありません。

したがって、「定数変数」は静的または非静的(インスタンス変数)の可能性があります。

ただし、JLSは、変数が「定数変数」であること(他に単なる最終的なものである必要はありません)について、他のいくつかの要件を課しています。

  • 文字列またはプリミティブのみであること
  • インラインのみで初期化されます。これは最終であり、空白の最終は許可されないため
  • "定数式" = "コンパイル時定数式"で初期化されます(下記のJLS引用を参照)

4.12.4。最終変数(JLS)

定数変数は、定数式で初期化されるプリミティブ型またはString型の最終的な変数です(§15.28 )

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)。

2
Code Complete

finalキーワードは、変数が一度だけ初期化されることを意味します。実際の定数もstaticとして宣言する必要があります。そのため、コンパイラによって定数として扱われる例はありません。それにもかかわらず、最後のキーワードは、(およびコンパイラーに)変数が(コンストラクター内で、または文字通り)1度だけ初期化されることを通知します。コンパイル時にそれらの値を割り当てる必要がある場合、フィールドは静的でなければなりません。

パフォーマンスはそれほど影響を受けませんが、プリミティブ型は不変であることを念頭に置いてください。プリミティブ型を作成すると、ガベージコレクターが削除するまでメモリにその値が保持されます。したがって、変数y = 1;があり、それをメモリ内でy = 2;に変更した場合、JVMは両方の値を持ちますが、変数は後者を「ポイント」します。

private int y = 10; //ここではyは最終ではありません

コンパイラーはコンパイル時定数と同じように扱われますか?

いいえ。これは、実行時に使用され、作成、初期化されたインスタンス変数です。

1
megathor

あります可能性があります非常に小さいパフォーマンスが低下一部private final int x = getX();のマシンには少なくとも1つのメソッド呼び出しが含まれるため(これはコンパイル時の定数ではないという事実に加えて)しかし、あなたが言ったように、それはnegligibleになるので、なぜわざわざ?

2番目の質問については、yは最終ではなく、実行時に変更される可能性があるため、コンパイル時定数ではありません。

1
Thomas

private final int x = getX();オブジェクトが初めて宣言されたときに呼び出されます。パフォーマンスの「低下」はgetX()に依存しますが、それはボトルネックを作成するようなものではありません。

0
UmNyobe

単純に言えば、コンパイラは、リファレンスパラメータを使用する代わりに、リファレンスを指定された実際の値に置き換えます。

public static void main(String[] args) {
final int x = 5;
}

すなわち。コンパイル中に、コンパイラは参照変数「x」を使用するよりも、コンパイルのために直接5の初期値をとります。

この説明を確認してください

0
Nick