30 votes

Pourquoi mon programme n'affiche-t #39'affiche-t l'erreur de temps de compilation lorsque la variable de classe finale n'est pas paraphée ?

Pour le code suivant:

public class StaticFinal
{
    private final static int i ;
    public StaticFinal()
    {}
}

Je reçois erreur de compilation:

StaticFinal.java:7: variable i might not have been initialized
        {}
         ^
1 error

Ce qui est en conformité avec JLS8.3.1.2 , qui reste que:

C'est une erreur de compilation si un vide final (§4.12.4) variable de classe n'est pas encore définitivement attribuées (§16.8) par un initialiseur statique (§8.7) de la classe dans laquelle elle est déclarée.

Donc , l'erreur ci-dessus est complètement compris.
Mais maintenant, considérez les points suivants :

public class StaticFinal
{
    private final static int i ;
    public StaticFinal()throws InstantiationException
    {
        throw new InstantiationException("Can't instantiate"); // Don't let the constructor to complete.
    }
}

Ici, le constructeur n'est jamais fini, car InstantiationException est jeté dans le milieu de constructeur. Et ce code compile bien!!
Pourquoi est-il? Pourquoi ce code ne s'affiche pas erreur de compilation sur le non-initialisation de final variable i ?


MODIFIER
Je suis pour le compiler à l'aide de javac 1.6.0_25 sur l'invite de commande ( ne Pas utiliser du tout de l'IDE )

3voto

Makoto Points 23751

Fait intéressant à noter, le code de la compilation de savoir si ou non le champ est marquée static - et à l'Ide, il va se plaindre (mais compiler) avec le champ statique, et de ne pas dire un mot à la non-champ statique.

Vous avez raison en ce que JLS §8.1.3.2 a certaines règles concernant [statique] final champs. Cependant, il ya quelques autres règles autour de final domaines qui jouent un grand rôle ici, venant de Java Language Specification §4.12.4 - qui précisent la compilation de la sémantique d'un final champ.

Mais avant que nous puissions entrer dans la boule de cire, nous avons besoin de déterminer ce qui arrive quand nous voyons throws - ce qui nous est donné par le§14.18, l'accent de la mine:

Une instruction throw provoque une exception (§11) à être jetés. Le résultat est un transfert immédiat de contrôle (§11.3) qui peuvent sortir plusieurs états et plusieurs de constructeur, de l'instance de l'initialiseur statique de l'initialiseur et champ d'initialiseur évaluations, et des invocations de méthode jusqu'à ce qu'un rapport d'essai (§14.20) est constaté que les captures de la valeur renvoyée. Si aucune instruction try est trouvé, puis l'exécution du thread (§17) qui a exécuté le jet est terminé (§11.3) après l'invocation de la uncaughtException méthode pour le groupe de thread auquel le thread appartient.

En d'autres termes - au cours de l'exécution, si nous rencontrons un throws déclaration, il peut interrompre l'exécution du constructeur (officiellement, "termine brusquement, provoquant ainsi l'objet pour ne pas être construit, ou construits dans un état incomplet. Ce pourrait être un trou de sécurité, selon la plate-forme et partielle de l'exhaustivité du constructeur.

Ce que la JVM s'attend, donné par le §4.5, c'est qu' un champ de ACC_FINAL ensemble n'a jamais la valeur est définie après la construction de l'objet:

Déclarée final; jamais directement affecté après la construction de l'objet (JLS §17.5).

Nous sommes donc dans un peu d'un cornichon - nous nous attendons à ce comportement de ce cours au moment de l'exécution, mais pas lors de la compilation. Et pourquoi ne IntelliJ élever une légère agitation quand j'ai static dans ce domaine, mais pas quand je ne le fais pas?

Tout d'abord, retour à l' throws - il n'y a qu'une erreur de compilation avec cette déclaration si l'un de ces trois pièces ne sont pas satisfaits:

  • L'expression levée est pas cochée ou null,
  • Vous try de catch de l'exception, et que vous êtes en catching avec le bon type, ou
  • L'expression levée est quelque chose qui peut effectivement être jeté, conformément à l'article 8.4.6 et §8.8.5.

Si la compilation d'un constructeur avec un throws est légitime. Il se trouve que, au moment de l'exécution, il sera toujours terminer brusquement.

Si une instruction throw est contenue dans un constructeur de déclaration, mais sa valeur n'est pas pris par certains essayent de l'énoncé qui le contient, puis la création de l'instance de la classe expression qui a appelé le constructeur se terminer brusquement à cause de la jeter (§15.9.4).

Maintenant, sur ce blanc final champ. Il y a un curieux morceau - leur affectation seulement après la fin du constructeur, l'accent leur.

Un vide final de la variable d'instance doit être définitivement attribuées (§16.9) à la fin de chaque constructeur (§8.8) de la classe dans laquelle elle est déclarée; sinon, une erreur de compilation se produit.

Si nous ne jamais atteindre la fin du constructeur?


Premier programme: Normal instanciation d'un static final champ, décompilé:

// class version 51.0 (51)
// access flags 0x21
public class com/stackoverflow/sandbox/DecompileThis {

    // compiled from: DecompileThis.java

    // access flags 0x1A
    private final static I i = 10

    // access flags 0x1
    public <init>()V
            L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
            L1
    LINENUMBER 9 L1
            RETURN // <- Pay close attention here.
    L2
    LOCALVARIABLE this Lcom/stackoverflow/sandbox/DecompileThis; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 1
}

Observons que nous avons fait appel à un RETURN instruction après le succès de l'appel de notre - <init>. De sens, et est parfaitement légal.

Deuxième programme: Jette dans le constructeur et le vide static final champ, décompilé:

// class version 51.0 (51)
// access flags 0x21
public class com/stackoverflow/sandbox/DecompileThis {

  // compiled from: DecompileThis.java

  // access flags 0x1A
  private final static I i

  // access flags 0x1
  public <init>()V throws java/lang/InstantiationException 
   L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 8 L1
    NEW java/lang/InstantiationException
    DUP
    LDC "Nothin' doin'."
    INVOKESPECIAL java/lang/InstantiationException.<init> (Ljava/lang/String;)V
    ATHROW // <-- Eeek, where'd my RETURN instruction go?!
   L2
    LOCALVARIABLE this Lcom/stackoverflow/sandbox/DecompileThis; L0 L2 0
    MAXSTACK = 3
    MAXLOCALS = 1
}

Les règles de l' ATHROW indiquent que la référence est sortie, et si il y a un gestionnaire d'exceptions, là, qui va contenir l'adresse de l'instruction sur le maniement de l'exception. Sinon, elle est retirée de la pile.

Nous n'avons jamais explicitement de retour, ce qui implique que nous n'avons jamais achever la construction de l'objet. Donc, l'objet peut être considéré comme un vilain demi-initialisé à l'état, tout en obéissant au moment de la compilation de règles - qui est, toutes les déclarations sont accessibles.

Dans le cas d'un champ statique, puisque ce n'est pas considéré comme une variable d'instance, mais une variable de classe, il ne semble pas mauvais que ce genre d'invocation est permis. Il peut être intéressant de déposer un rapport de bogue.


À bien y penser, il ne prend un sens dans le contexte, depuis la déclaration suivante en Java est légal, et le corps de méthode sont congruents de constructeur de corps:

public boolean trueOrDie(int val) {
    if(val > 0) {
        return true;
    } else {
        throw new IllegalStateException("Non-natural number!?");
    }
}

1voto

Andrea Points 707

Comme je suis à la compréhension, ici, nous sommes tous les développeurs, donc, je crois que nous ne trouverons pas la vraie réponse parmi nous...cette chose a quelque chose à voir avec le compilateur internes...et je pense que c'est un bug, ou au moins un comportement indésirable.

À l'exclusion de l'Éclipse, qui a une sorte de différentiels compilateur (et est donc en mesure de détecter immédiatement le problème), la ligne de commande javac effectue un one-shot compilation. Maintenant, le premier extrait

public class StaticFinal {
    private final static int i ;
}

qui est fondamentalement la même chose que d'avoir un constructeur vide (comme dans le premier exemple), est en train de jeter l'erreur de compilation, et c'est bien parce que c'est le respect de spécifications.

Dans le deuxième extrait, je pense qu'il y a un bug dans le compilateur; il semble que le compilateur fait quelques décisions basées sur ce que le constructeur est en train de faire. C'est plus évident si vous essayez de compiler celui-ci,

public class StaticFinal
{
    private final static int i ;

    public StaticFinal() 
    {
        throw new RuntimeException("Can't instantiate"); 
    }
}

C'est plus bizarre que ton exemple parce que le décochée exception n'est pas déclarée dans la signature de la méthode et sera (du moins c'est ce que je pensais avant de lire ce post) découvert seulement au moment de l'exécution.

En observant le comportement que je pourrais dire (mais qui est mal selon les spécifications) que.

Les variables final, le compilateur essaie de voir si elles sont explicitement initialisée, ou initialisés dans une statique intializer bloc, mais, pour une raison étrange, il cherche quelque chose dans le constructeur de trop:

  • si ils sont initialisés dans le constructeur, le compilateur génère une erreur (vous ne pouvez pas attribuer il y a une valeur pour une dernière variable statique)
  • si le constructeur est vide, le compilateur génère une erreur (si vous compilez le premier exemple, l'un avec l'explicite zéro argument du constructeur, le compilateur pauses indiquant le crochet de fermeture du constructeur comme la ligne d'erreur).
  • si la classe ne peut pas être instanciée parce que le constructeur n'est pas complète parce que une exception est levée (ce n'est pas vrai, par exemple, si vous écrivez.exit(1) au lieu de lancer une exception...il ne compile pas!), ensuite, la valeur par défaut sera affectée à la variable statique (!)

0voto

AmirHd Points 1004

Après l'ajout d'une méthode principale pour rendre le code imprimer i. Le code imprime la valeur 0. Cela implique que le compilateur java initialise le je avec la valeur 0 automatiquement. Je l'ai écrit dans l'Ide, et avait pour désactiver le code de vérification pour être en mesure de générer le code. Sinon, il ne serait pas me laisser me donne le même message d'erreur que vous aviez avant de jeter l'exception.

Le code JAVA: non initialisé

public class StaticFinal {
    private final static int i;
    public StaticFinal(){
        throw new InstantiationError("Can't instantiate!");
    }

    public static void main(String args[]) {
        System.out.print(i);
    }

}

Décompilé

Identique

Le code JAVA: Initialisé

public class StaticFinal {
    private final static int i = 0;
    public StaticFinal(){
        throw new InstantiationError("Can't instantiate!");
    }

    public static void main(String args[]) {
        System.out.print(StaticFinal.i);
    }

}

Décompilé

public class StaticFinal
{

    public StaticFinal()
    {
        throw new InstantiationError("Can't instantiate!");
    }

    public static void main(String args[])
    {
        System.out.print(0);
    }

    private static final int i = 0;
}

Après la décompilation du code, il s'avère que ce n'est pas le cas. Comme le décompilé les codes et l'original sont identiques. La seule autre possibilité est que l'initialisation se fait par la Machine Virtuelle Java. La dernière modification que j'ai faite est une assez bonne preuve que c'est le cas.

Dois dire que bien sur vous pour repérer ce.

Questions connexes: Ici

-2voto

Marconius Points 464

Je dis-le est tout simplement parce que lorsque vous ajoutez le , vous êtes essentiellement la manipulation de `` l'erreur, de sorte que le compilateur va "oh, eh bien, il sait probablement ce qu'il fait alors". Il donne toujours une erreur de temps d'exécution, après tout.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X