Question Les variables Java Final auront-elles des valeurs par défaut?


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'essaie de l'exécuter, j'obtiens une erreur de compilation en tant que: variable x might not have been initialized basé sur les valeurs par défaut de java, 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 s'il vous plaît expliquer ce comportement ..


74
2017-07-28 07:49


origine


Réponses:


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 voir, une fois qu'une instance a été créée, le champ final n'a pas été définitivement assignéalors que (de http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):

Une variable d’instance finale vierge doit être définitivement attribuée à   la fin de chaque constructeur de la classe dans laquelle il se trouve   déclaré; sinon, une erreur de compilation se produit.

Bien que cela ne semble pas être expliqué de manière explicite dans les docs (du moins, je n'ai pas pu le trouver), un dernier champ doit 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.

Les 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 de code, x est initialisé lors de la création de l'instance, de sorte que 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 par 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");
}

57
2017-07-28 08:03



JLS est en disant que vous doit attribuer la valeur par défaut à la variable d’instance finale vide dans le constructeur (ou dans bloc d'initialisation ce qui est assez similaire). C'est pourquoi vous obtenez l'erreur dans le premier cas. Cependant, il ne dit pas que vous ne pouvez pas y accéder en constructeur avant. Cela 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 que chaque valeur doit être définitivement assigné 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 aussi un 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 deuxième cas, la valeur x est définitivement assigné au début du constructeur, car il contient l'affectation à la fin de celui-ci.


27
2017-07-28 08:08



Si vous n'initialisez pas x vous obtiendrez une erreur de compilation depuis x n'est jamais initialisé.

Déclarer x comme final signifie qu'il ne peut être initialisé que dans le constructeur ou dans bloc d'initialisation (puisque ce bloc sera copié par le compilateur dans chaque constructeur).

La raison pour laquelle vous obtenez 0 imprimé avant que la variable est initialisée est due au comportement défini dans le Manuel(voir section "Valeurs par défaut"):

Les valeurs par défaut

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 sur   valeur raisonnable par défaut du compilateur. De manière générale, ce défaut   sera nul ou nul, selon le type de données. S'appuyant sur un tel   les valeurs par défaut, cependant, sont généralement considérées comme une mauvaise programmation   style.

Le tableau suivant résume les valeurs par défaut pour les données ci-dessus   les types.

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

7
2017-07-28 08:06



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 avez du code pour lui attribuer une valeur, mais la séquence d'exécution signifie que vous faites référence au champ avant et après son attribution.

La valeur pré-assignée de n'importe quel champ est la valeur par défaut.


4
2017-07-28 08:19



Tous les champs non finaux d'une classe sont initialisés à une valeur par défaut (0 pour les types de données numériques, false pour booléen et null pour les types de référence, parfois appelés objets complexes). Ces champs initialisent avant qu'un constructeur (ou un bloc d'initialisation d'instance) s'exécute indépendamment du fait que les champs aient été déclarés avant ou après le constructeur.

Final les 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 aucune 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.


2
2017-07-28 08:06



Laissez-moi le mettre dans les mots les plus simples possible.

final les variables doivent être initialisées, ceci est requis 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 de l'initialiser 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 appelle à son tour 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 blocage de l'initialiseur, la variable finale reste non initialisée, ce qui est une erreur détectée par le compilateur.

Dans cas 2: L'initialiseur initialisera la variable finale, le compilateur sait donc qu'avant l'initialisation de l'objet, 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 en est que le compilateur sait déjà qu’il n’ya pas d’erreur et lors de l’invocation de la méthode init, toutes les finales seront initialisées aux valeurs par défaut, et un drapeau défini pourra être modifié selon une instruction d’affectation similaire à x=7. Voir l'invocation init ci-dessous:

enter image description here


1
2017-07-28 08:13



Autant que je sache, le compilateur initialisera toujours les variables de classe à des valeurs par défaut (même les variables finales). Par exemple, si vous deviez initialiser un int sur lui-même, l'int serait défini sur 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 imprimera ce qui suit:

Here x is 0
Here x is 0
const called

1
2017-07-28 08:05



Si j'essaie de l'exécuter, j'obtiens une erreur de compilation car: la variable x n'a peut-être pas été initialisée en fonction des valeurs par défaut de Java.

"Ici, x vaut 0".

Vous ne voyez pas cette sortie car vous obtenez une erreur de compilation en premier lieu. Les variables finales obtiennent une valeur par défaut, mais la spécification JLS (Java Language Specification) exige que vous les initialisiez à la fin du constructeur (LE: j'inclus ici les blocs d'initialisation), sinon vous obtiendrez une erreur de compilation empêchera votre code d'être compilé et exécuté.

Votre deuxième exemple respecte l'exigence, c'est pourquoi (1) votre code compile et (2) vous obtenez le comportement attendu.

Dans le futur, essayez de vous familiariser avec le JLS. Il n'y a pas de meilleure source d'informations sur le langage Java.


1
2017-07-28 22:09