Certains peuvent le trouver similaire à la question SO Les variables Java Final auront-elles des valeurs par défaut? mais cette réponse ne résout pas complètement ce problème, car cette question n’imprime pas directement la valeur de x dans le bloc d’initialisation d’instance.
Le problème se pose lorsque j’essaie d’imprimer x directement dans le bloc d’initialisation de l’instance, tout en atsortingbuant une valeur à x avant la fin du bloc:
class HelloWorld { final int x; { System.out.println(x); x = 7; System.out.println(x); } HelloWorld() { System.out.println("hi"); } public static void main(Ssortingng[] args) { HelloWorld t = new HelloWorld(); } }
Cela donne une erreur de compilation indiquant que la variable x n’a peut-être pas été initialisée.
$ javac HelloWorld.java HelloWorld.java:6: error: variable x might not have been initialized System.out.println(x); ^ 1 error
Au lieu d’imprimer directement, j’appelle une fonction à imprimer:
class HelloWorld { final int x; { printX(); x = 7; printX(); } HelloWorld() { System.out.println("hi"); } void printX() { System.out.println(x); } public static void main(Ssortingng[] args) { HelloWorld t = new HelloWorld(); } }
Cela comstack correctement et donne des résultats
0 7 hi
Quelle est la différence conceptuelle entre les deux cas?
Dans le JLS, §8.3.3. Renvoi des références lors de l’initialisation des champs , il est indiqué qu’il y a une erreur de compilation lorsque:
Utilisation de variables d’instance dont les déclarations apparaissent textuellement après utilisation est parfois restreinte, même si ces variables d’instance sont dans la scope. Plus précisément, il s’agit d’une erreur de compilation si toutes les conditions suivantes sont remplies:
La déclaration d’une variable d’instance dans une classe ou une interface C apparaît textuellement après une utilisation de la variable d’instance;
L’utilisation est un nom simple dans un initialiseur de variable d’instance de C ou un initialiseur d’instance de C;
L’utilisation n’est pas du côté gauche d’une affectation;
C est la classe ou l’interface la plus interne contenant l’utilisation.
Les règles suivantes sont accompagnées de quelques exemples, dont le plus proche est le votre:
class Z { static int peek() { return j; } static int i = peek(); static int j = 1; } class Test { public static void main(Ssortingng[] args) { System.out.println(Zi); } }
Les access [aux variables statiques ou d’instance] par les méthodes ne sont pas vérifiés de cette manière , le code ci-dessus produit donc la sortie 0
, car la variable initializer for i
utilise la méthode de classe peek()
pour accéder à la valeur j
avant j
initialisé par sa variable initializer, à quel point il a toujours sa valeur par défaut ( §4.12.5 Valeurs initiales des variables ).
Donc, pour résumer, votre deuxième exemple comstack et exécute correctement, car le compilateur ne vérifie pas si la variable x
a déjà été initialisée lorsque vous printX()
et que printX()
a lieu à Runtime, la variable x
sera assignée avec sa valeur par défaut ( 0
).
En lisant le JLS, la réponse semble être dans la section 16.2.2 :
Un champ de membre
final
blanc vide est définitivement atsortingbué (et de plus n’est pas définitivement atsortingbué) avant le bloc (§14.2) qui est le corps de toute méthode dans le domaine deV
et avant la déclaration de toute classe déclarée dans le cadre deV
Cela signifie que lorsqu’une méthode est appelée, le champ final est assigné à sa valeur par défaut 0 avant de l’appeler. Par conséquent, lorsque vous la référencez dans la méthode, elle comstack avec succès et affiche la valeur 0.
Cependant, lorsque vous accédez au champ en dehors d’une méthode, il est considéré comme non atsortingbué, d’où l’erreur de compilation. Le code suivant ne sera pas non plus compilé:
public class Main { final int x; { method(); System.out.println(x); x = 7; } void method() { } public static void main(Ssortingng[] args) { } }
car:
V
est assigné [un] avant toute autre instructionS
du bloc ssiV
est [un] assigné après l’instruction précédant immédiatementS
dans le bloc.
Le champ final x
n’étant pas atsortingbué avant l’invocation de la méthode, il n’est toujours pas atsortingbué après.
Cette note dans le JLS est également pertinente:
Notez qu’il n’y a pas de règles qui nous permettent de conclure que
V
n’est définitivement pas atsortingbué avant le bloc qui est le corps de tout constructeur, méthode, initialiseur d’instance ou initialiseur statique déclaré dansC
Nous pouvons conclure de manière informelle queV
n’est pas définitivement non affecté avant le bloc qui est le corps de tout constructeur, méthode, initialiseur d’instance ou initialiseur statique déclaré dans C, mais il n’est pas nécessaire qu’une telle règle soit explicitée.
La différence est que dans le premier cas, vous appelez System.out.println
partir du bloc d’initialisation, donc le bloc qui est appelé avant constructeur. Dans la première ligne
System.out.println(x);
La variable x
n’est pas encore initialisée pour que vous obteniez une erreur de compilation.
Mais dans le second cas, vous appelez une méthode d’instance qui ne sait pas si la variable a déjà été initialisée pour ne pas avoir d’erreur de compilation et vous pouvez voir la valeur par défaut pour x
Ok, voici mes 2 centimes.
Nous soaps tous que les variables finales ne peuvent être initialisées que lors de la déclaration ou ultérieurement dans les constructeurs. Gardez cela à l’esprit, laissez voir ce qui s’est passé jusqu’ici.
Aucune erreur
Donc, lorsque vous utilisez une méthode, elle a déjà une valeur.
1) If you initialize it, that value. 2) If not, the default value of data type.
Cas d’erreur:
Lorsque vous faites cela dans un bloc d’initialisation, vous voyez des erreurs.
Si vous regardez les docs of initialization block
{ // whatever code is needed for initialization goes here }
et
Le compilateur Java copie les blocs d’initialisation dans chaque constructeur. Par conséquent, cette approche peut être utilisée pour partager un bloc de code entre plusieurs constructeurs.
Dans l’oeil du compilateur, votre code est littéralement égal à
class HelloWorld { final int x; HelloWorld() { System.out.println(x); ------------ ERROR here obviously x = 7; System.out.println(x); System.out.println("hi"); } public static void main(Ssortingng[] args) { HelloWorld t = new HelloWorld(); } }
Vous l’utilisez avant même de l’initialiser.
Cas 1 :
Vous donne une erreur de compilation,
Parce que chez System.out.println(x);
vous essayez d’imprimer x qui n’a jamais été initialisé.
Cas 2:
Fonctionne parce que vous n’utilisez pas directement de valeurs littérales, au lieu de cela, vous appelez une méthode, ce qui est correct.
Règle générale est,
Si vous essayez d’accéder à une variable qui n’est jamais initialisée, cela donnera une erreur de compilation.
Nous traitons ici avec le bloc d’initialisation. Le compilateur Java copie les blocs d’initialisation dans chaque constructeur.
L’erreur du compilateur ne se produit pas dans le deuxième exemple, car l’impression x est dans un autre cadre, veuillez vous reporter à la spécification.