Schauen Sie sich den folgenden Code an:
class Foo{
public static int x = 1;
}
class Bar{
public static void main(String[] args) {
Foo foo;
System.out.println(foo.x); // Error: Variable 'foo' might not have been initialized
}
}
Wie Sie sehen , während für den Zugriff statischen Feldes versuchen x
über einen nicht initialisierte lokalen Variable Foo foo;
Code foo.x
erzeugt Übersetzungsfehler: Variable 'foo' might not have been initialized
.
Es könnte scheinen , wie dieser Fehler macht Sinn, aber nur , bis wir erkennen , dass ein Zugriff auf static
Mitglied die JVM nicht wirklich verwendet den Wert einer Variablen, sondern nur seine Art .
Zum Beispiel kann ich foo
mit value initialisieren null
und dies ermöglicht uns den x
problemlosen Zugriff :
Foo foo = null;
System.out.println(foo.x); //compiles and at runtime prints 1!!!
Ein solches Szenario funktioniert, weil der Compiler erkennt, dass x
es statisch ist und so behandelt, foo.x
wie es geschrieben wurde Foo.x
(zumindest dachte ich das bis jetzt).
Warum besteht der Compiler plötzlich darauf foo
, einen Wert zu haben, den er überhaupt nicht verwendet ?
Haftungsausschluss: Dies ist kein Code, der in einer realen Anwendung verwendet werden würde, sondern ein interessantes Phänomen, auf das ich bei Stack Overflow keine Antwort finden konnte. Deshalb habe ich mich entschlossen, danach zu fragen.
quelle
static
Member-Compiler nicht der Wert der Variablen, sondern deren Typ verwendet wird . Wir können sogar schreiben((Foo)null).x
und dies wird kompiliert und funktioniert, da der Compiler erkennt, dass diesx
statisch ist (es sei denn, ich habe Ihren Kommentar falsch verstanden).foo.x
) sollte ein Compilerfehler gewesen sein, als Java zum ersten Mal erstellt wurde. Leider ist dieses Schiff vor über 25 Jahren gesegelt und wäre eine bahnbrechende Veränderung, wenn sie es jetzt ändern würden.Foo.x
ist bevorzugt (weshalb wir in der Regel Kompilation bekommen Warnung , wenn wir verwenden Variante versuchenfoo.x
).Antworten:
§15.11. Feldzugriffsausdrücke :
Wo früher angegeben, dass der Feldzugriff durch identifiziert wird
Primary.Identifier
.Dies zeigt, dass, obwohl es das nicht zu verwenden scheint
Primary
, es dennoch ausgewertet wird und das Ergebnis dann verworfen wird, weshalb es initialisiert werden muss. Dies kann einen Unterschied machen, wenn die Auswertung den Zugriff wie im Angebot angegeben anhält.BEARBEITEN:
Hier ist ein kurzes Beispiel, um nur visuell zu demonstrieren, dass
Primary
das Ergebnis ausgewertet wird, obwohl das Ergebnis verworfen wird:class Foo { public static int x = 1; public static Foo dummyFoo() throws InterruptedException { Thread.sleep(5000); return null; } public static void main(String[] args) throws InterruptedException { System.out.println(dummyFoo().x); System.out.println(Foo.x); } }
Hier sehen Sie, dass dies
dummyFoo()
immer noch ausgewertet wird, da sich dasprint
um 5 Sekunden verzögertThread.sleep()
, obwohl immer einnull
Wert zurückgegeben wird, der verworfen wird.Wenn der Ausdruck nicht ausgewertet wurde
print
, wird er sofort angezeigt. Dies wird angezeigt, wenn die KlasseFoo
direkt für den Zugriffx
mit verwendet wirdFoo.x
.Hinweis: Der Methodenaufruf wird auch
Primary
in §15.8 Primäre Ausdrücke gezeigt .quelle
javac
wird dies wörtlich, indem eine Lade- und Pop-Anweisung generiert wird, währendecj
die formale Regel erzwungen wird, dh kein Zugriff über eine nicht initialisierte Variable zugelassen wird, aber kein Code für die nebenwirkungsfreie Operation generiert wird.Es spielt keine Rolle, auf was Sie über eine lokale Variable zugreifen möchten. Die Regel ist, dass es vorher definitiv zugewiesen werden sollte.
Um einen Feldzugriffsausdruck auszuwerten
foo.x
, muss zuerst derprimary
Teil von it (foo
) ausgewertet werden.foo
Dies bedeutet, dass der Zugriff auf erfolgt, was zu einem Fehler bei der Kompilierung führt.quelle
Es ist sinnvoll, die Regeln so einfach wie möglich zu halten, und „Verwenden Sie keine Variable, die möglicherweise nicht initialisiert wurde“ ist so einfach wie es nur geht.
Genauer gesagt gibt es eine etablierte Methode zum Aufrufen statischer Methoden - verwenden Sie immer den Klassennamen, keine Variable.
Die Variable "foo" ist unerwünschter Overhead, der entfernt werden sollte, und die Compilerfehler und Warnungen könnten als hilfreich angesehen werden, um dies zu erreichen.
quelle
Andere Antworten erklären perfekt den Mechanismus hinter dem, was passiert. Vielleicht wollten Sie auch die Gründe für Javas Spezifikation. Da ich kein Java-Experte bin, kann ich Ihnen nicht die ursprünglichen Gründe nennen, aber lassen Sie mich darauf hinweisen:
Foo.x
ist es natürlich , weil eine Instanz unnötig ist.)foo.x
(Zugriff über Instanzvariable)?Foo.x
bereits "einfacher Zugriffx
" bedeutet, ist es vernünftig, dass der Ausdruckfoo.x
eine andere Bedeutung hat. Das heißt, jeder Teil des Ausdrucks ist gültig und zugänglichx
.Hoffen wir, dass jemand, der sich auskennt, den wahren Grund erkennen kann. :-)
quelle