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 catch
ing 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!?");
}
}