私はこのようなプログラムを持っています:
class Test {
final int x;
{
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
実行しようとすると、コンパイラエラーが発生します:variable x might not have been initialized
に基づくJavaデフォルト値私は以下の出力を取得する必要がありますか??
"Here x is 0".
最終変数にはdafault値がありますか?
このようにコードを変更すると、
class Test {
final int x;
{
printX();
x = 7;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
私は次のように出力を得ています:
Here x is 0
Here x is 7
const called
誰でもこの動作を説明できますか?.
http://docs.Oracle.com/javase/tutorial/Java/javaOO/initial.html 、「インスタンスメンバーの初期化」の章:
Javaコンパイラは、初期化子ブロックをすべてのコンストラクタにコピーします。
それは言うことです:
{
printX();
}
Test() {
System.out.println("const called");
}
次のように動作します。
Test() {
printX();
System.out.println("const called");
}
このように、インスタンスが作成されると、最終フィールドは definitely assigned ではなく、while(from http://docs.Oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2 ):
空白の最終インスタンス変数は、それが宣言されているクラスのすべてのコンストラクターの最後に必ず割り当てる必要があります。そうしないと、コンパイル時エラーが発生します。
ドキュメントには明確に記載されていないようですが(少なくとも私はそれを見つけることができませんでした)、最終フィールドはコンストラクタの終了前に一時的にデフォルト値を取得する必要があるため、 予測可能値 割り当て前に読んだ場合.
デフォルト値: http://docs.Oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
2番目のスニペットでは、インスタンスの作成時にxが初期化されるため、コンパイラーは文句を言いません。
Test() {
printX();
x = 7;
printX();
System.out.println("const called");
}
また、次のアプローチは機能しないことに注意してください。 final変数のデフォルト値の使用は、メソッドを介してのみ許可されます。
Test() {
System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
x = 7;
System.out.println("Here x is " + x);
System.out.println("const called");
}
[〜#〜] jls [〜#〜]は 言う あなたコンストラクター(または初期化ブロックとほぼ同じ)の空の最終インスタンス変数にデフォルト値を割り当てます。そのため、最初のケースでエラーが発生します。ただし、以前にコンストラクタでアクセスできないとは言っていません。少し変に見えますが、割り当て前にアクセスして、int-0のデフォルト値を確認できます。
UPD。@ I4mpiで述べたように、[〜#〜] jls [〜#〜]定義 各値にアクセスする前に各値を明確に割り当てるルール:
Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.
ただし、コンストラクタとフィールドに関して 興味深いルール もあります。
If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.
そのため、2番目の場合、値x
は、コンストラクターの最後に割り当てが含まれているため、コンストラクターの最初にに確実に割り当てられます 。
x
は初期化されないため、x
を初期化しないと、コンパイル時エラーが発生します。
x
を最終として宣言すると、コンストラクターまたは initializer-block でのみ初期化できることを意味します(このブロックはコンパイラーによってすべてのコンストラクターにコピーされるため)。
0
変数が初期化される前に出力されるのは、 manual で定義された動作によるものです(「デフォルト値」を参照)セクション):
フィールドが宣言されるときに値を割り当てる必要は必ずしもありません。宣言されているが初期化されていないフィールドは、コンパイラによって適切なデフォルトに設定されます。一般的に、このデフォルトはデータ型に応じてゼロまたはヌルになります。ただし、このようなデフォルト値に依存することは、一般にプログラミングスタイルが悪いと見なされます。
次のグラフは、上記のデータ型のデフォルト値をまとめたものです。
Data Type Default Value (for fields)
--------------------------------------
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
String (or any object) null
boolean false
最初のエラーは、最終フィールドはあるが、それを初期化するためのコードがないという不平を言っているコンパイラーです-簡単です。
2番目の例では、値を割り当てるコードがありますが、実行シーケンスは、フィールドを割り当てる前と後の両方でフィールドを参照することを意味します。
任意のフィールドに事前に割り当てられた値がデフォルト値です。
クラスのすべての非最終フィールドは、デフォルト値に初期化されます(数値データ型の場合は0
、ブール型の場合はfalse
、参照型の場合はnull
(複合オブジェクトとも呼ばれます)。これらのフィールドは、コンストラクター(またはインスタンス初期化ブロック)が実行される前に、コンストラクターの前または後にフィールドが宣言されたかどうかに関係なく初期化されます。
最終クラスのフィールドにはデフォルト値なしがあり、クラスコンストラクターがジョブを完了する前に一度だけ明示的に初期化する必要があります。
実行ブロック(メソッドなど)内のローカル変数にはデフォルト値がありません。これらのフィールドは、最初に使用する前に明示的に初期化する必要があり、ローカル変数が最終としてマークされているかどうかは関係ありません。
できるだけ簡単な言葉で説明させてください。
final
変数を初期化する必要があります。これは、言語仕様で義務付けられています。そうは言っても、宣言時に初期化する必要はないことに注意してください。
オブジェクトを初期化する前に、初期化する必要があります。
初期化ブロックを使用して、最終変数を初期化できます。現在、初期化ブロックは、2つのタイプstatic
とnon-static
使用したブロックは、非静的初期化ブロックです。したがって、オブジェクトを作成すると、ランタイムはコンストラクターを呼び出し、そのコンストラクターが親クラスのコンストラクターを呼び出します。
その後、すべての初期化子(あなたの場合は非静的な初期化子)を呼び出します。
あなたの質問では、case 1:初期化ブロックの完了後でも、最終変数は初期化されずに残ります。これはエラーコンパイラが検出します。
ケース2の場合:初期化子は最終変数を初期化するため、コンパイラはオブジェクトが初期化される前に、最終変数がすでに初期化されていることを認識します。したがって、文句を言うことはありません。
ここで問題なのは、なぜx
がゼロを取るのかということです。ここでの理由は、コンパイラーがエラーがないことをすでに知っているため、initメソッドを呼び出すと、すべてのファイナルがデフォルトに初期化され、実際の割り当てステートメントでx=7
。以下のinit呼び出しを参照してください。
私がそれを実行しようとすると、コンパイラエラーが発生します:変数xはJavaデフォルト値に基づいて初期化されていない可能性があります
「ここでxは0」です。
いいえ。そもそもコンパイル時エラーが発生しているため、その出力は表示されません。最終的な変数はデフォルト値を取得しますが、Java Language Specification(JLS)では、コンストラクターの最後までに初期化する必要があります(LE:ここに初期化ブロックを含めます) 'コンパイル時エラーが発生し、コードがコンパイルおよび実行されなくなります。
2番目の例では要件を尊重しているため、(1)コードがコンパイルされ、(2)期待される動作が得られます。
将来的には、JLSに慣れるようにしてください。 Java言語に関するより良い情報源はありません。
私の知る限り、コンパイラは常にクラス変数をデフォルト値に初期化します(最終変数も)。たとえば、intをそれ自体に初期化する場合、intはデフォルトの0に設定されます。以下を参照してください。
class Test {
final int x;
{
printX();
x = this.x;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
上記は次を印刷します:
Here x is 0
Here x is 0
const called