J'ai un programme comme celui-ci:
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();
}
}
Si j'essaye de l'exécuter, j'obtiens une erreur de compilation comme: variable x might not have been initialized
basé sur Java valeurs par défaut je devrais obtenir la sortie ci-dessous à droite ??
"Here x is 0".
Les variables finales auront-elles des valeurs par défaut?
si je change mon code comme ça,
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();
}
}
Je reçois une sortie en tant que:
Here x is 0
Here x is 7
const called
Quelqu'un peut-il expliquer ce comportement?.
http://docs.Oracle.com/javase/tutorial/Java/javaOO/initial.html , chapitre "Initialisation des membres d'instance":
Le compilateur Java copie les blocs d'initialisation dans chaque constructeur.
C'est-à-dire:
{
printX();
}
Test() {
System.out.println("const called");
}
se comporte exactement comme:
Test() {
printX();
System.out.println("const called");
}
Comme vous pouvez ainsi le voir, une fois qu'une instance a été créée, le champ final n'a pas été définitivement attribué , tandis que (de http://docs.Oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2 ):
Une variable d'instance finale vide doit être définitivement affectée à la fin de chaque constructeur de la classe dans laquelle elle est déclarée; sinon, une erreur de compilation se produit.
Bien qu'il ne semble pas être indiqué de manière explicite dans les documents (au moins, je n'ai pas pu le trouver), un champ final doit temporairement prendre sa valeur par défaut avant la fin du constructeur, de sorte qu'il ait un valeur prévisible si vous le lisez avant son affectation.
Valeurs par défaut: http://docs.Oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
Sur votre deuxième extrait, x est initialisé lors de la création de l'instance, donc le compilateur ne se plaint pas:
Test() {
printX();
x = 7;
printX();
System.out.println("const called");
}
Notez également que l'approche suivante ne fonctionne pas. L'utilisation de la valeur par défaut de la variable finale n'est autorisée que via une méthode.
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 [~ # ~] est en disant que vous devez attribuez la valeur par défaut à la variable d'instance finale vide dans le constructeur (ou dans bloc d'initialisation qui est à peu près la même). C'est pourquoi vous obtenez l'erreur dans le premier cas. Cependant, cela ne dit pas que vous ne pouvez pas y accéder dans le constructeur avant. Semble un peu bizarre, mais vous pouvez y accéder avant l'affectation et voir la valeur par défaut pour int - 0.
UPD. Comme mentionné par @ I4mpi, [~ # ~] jls [~ # ~] définit la règle selon laquelle chaque valeur doit être définitivement attribuée avant tout accès:
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.
Cependant, il a également une règle intéressante en ce qui concerne les constructeurs et les champs:
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.
Donc, dans le second cas, la valeur x
est définitivement attribuée au début du constructeur, car elle contient l'affectation à la fin de celui-ci .
Si vous n'initialisez pas x
, vous obtiendrez une erreur de compilation car x
n'est jamais initialisé.
Déclarer x
comme final signifie qu'il ne peut être initialisé que dans le constructeur ou dans initializer-block (puisque ce bloc sera copié par le compilateur dans chaque constructeur).
La raison pour laquelle vous obtenez 0
imprimé avant l'initialisation de la variable est dû au comportement défini dans le manuel (voir: "Valeurs par défaut" section):
Il n'est pas toujours nécessaire d'attribuer une valeur lorsqu'un champ est déclaré. Les champs déclarés mais non initialisés seront définis par défaut par le compilateur. De manière générale, cette valeur par défaut sera zéro ou nulle, selon le type de données. Cependant, le recours à de telles valeurs par défaut est généralement considéré comme un mauvais style de programmation.
Le graphique suivant résume les valeurs par défaut pour les types de données ci-dessus.
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
La première erreur est le compilateur se plaignant que vous avez un champ final, mais pas de code pour l'initialiser - assez simple.
Dans le deuxième exemple, vous disposez d'un code pour lui affecter une valeur, mais la séquence d'exécution signifie que vous référencez le champ avant et après son affectation.
La valeur pré-affectée de n'importe quel champ est la valeur par défaut.
Tous les champs non finaux d'une classe s'initialisent à une valeur par défaut (0
pour les types de données numériques, false
pour les booléens et null
pour les types de référence, parfois appelés objets complexes). Ces champs s'initialisent avant qu'un constructeur (ou un bloc d'initialisation d'instance) s'exécute indépendamment du fait que les champs ont été déclarés avant ou après le constructeur.
Final champs d'une classe a pas de valeur par défaut et doit être explicitement initialisé une seule fois avant qu'un constructeur de classe ait terminé son travail.
Les variables locales à l'intérieur d'un bloc d'exécution (par exemple, une méthode) n'ont pas de valeur par défaut. Ces champs doivent être explicitement initialisés avant leur première utilisation et peu importe que la variable locale soit marquée comme finale ou non.
Permettez-moi de le dire dans les mots les plus simples possibles.
final
les variables doivent être initialisées, c'est obligatoire par la spécification de langue. Cela dit, veuillez noter qu'il n'est pas nécessaire de l'initialiser au moment de la déclaration.
Il est nécessaire d'initialiser cela avant que l'objet ne soit initialisé.
Nous pouvons utiliser des blocs d'initialisation pour initialiser les variables finales. Maintenant, les blocs d'initialisation sont de deux types static
et non-static
Le bloc que vous avez utilisé est un bloc d'initialisation non statique. Ainsi, lorsque vous créez un objet, Runtime invoque le constructeur et qui à son tour invoque le constructeur de la classe parente.
Après cela, il invoquera tous les initialiseurs (dans votre cas, l'initialiseur non statique).
Dans votre question, cas 1: Même après la fin du bloc d'initialisation, la variable finale reste non initialisée, ce qui est détecté par un compilateur d'erreur.
Dans cas 2: l'initialiseur initialisera la variable finale, donc le compilateur sait qu'avant que l'objet ne soit initialisé, la finale est déjà initialisée. Par conséquent, il ne se plaindra pas.
Maintenant, la question est de savoir pourquoi x
prend un zéro. La raison ici est que le compilateur sait déjà qu'il n'y a pas d'erreur et donc lors de l'invocation de la méthode init, toutes les finales seront initialisées aux valeurs par défaut, et un indicateur défini qu'elles peuvent changer lors d'une instruction d'affectation réelle similaire à x=7
. Voir l'invocation init ci-dessous:
Si j'essaie de l'exécuter, j'obtiens une erreur de compilation comme: la variable x n'a peut-être pas été initialisée en fonction de Java valeurs par défaut, je devrais obtenir la sortie ci-dessous à droite ??
"Ici x vaut 0".
Non. Vous ne voyez pas cette sortie car vous obtenez en premier lieu une erreur de compilation. Les variables finales obtiennent une valeur par défaut, mais la spécification de langage Java JLS) vous oblige à les initialiser à la fin du constructeur (LE: j'inclus les blocs d'initialisation ici), sinon vous 'obtiendra une erreur de compilation qui empêchera votre code d'être compilé et exécuté.
Votre deuxième exemple respecte l'exigence, c'est pourquoi (1) votre code se compile et (2) vous obtenez le comportement attendu.
À l'avenir, essayez de vous familiariser avec le JLS. Il n'y a pas de meilleure source d'informations sur la langue Java.
Pour autant que je sache, le compilateur initialisera toujours les variables de classe aux valeurs par défaut (même les variables finales). Par exemple, si vous deviez initialiser un int à lui-même, l'int serait réglé sur sa valeur par défaut de 0. Voir ci-dessous:
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();
}
}
Ce qui précède imprimerait ce qui suit:
Here x is 0
Here x is 0
const called