Gab es einen Grund, warum die Entwickler von Java der Meinung waren, dass lokalen Variablen kein Standardwert zugewiesen werden sollte? Im Ernst, wenn Instanzvariablen einen Standardwert erhalten können, warum können wir dann nicht dasselbe für lokale Variablen tun?
Und es führt auch zu Problemen, wie in diesem Kommentar zu einem Blog-Beitrag erklärt :
Nun, diese Regel ist am frustrierendsten, wenn versucht wird, eine Ressource in einem finally-Block zu schließen. Wenn ich die Ressource innerhalb eines Versuchs instanziiere, aber versuche, sie innerhalb des Endes zu schließen, wird dieser Fehler angezeigt. Wenn ich die Instanziierung außerhalb des Versuchs verschiebe, wird ein weiterer Fehler angezeigt, der besagt, dass a sich innerhalb eines Versuchs befinden muss.
Sehr frustrierend.
quelle
Antworten:
Lokale Variablen werden meistens deklariert, um eine Berechnung durchzuführen. Es ist also die Entscheidung des Programmierers, den Wert der Variablen festzulegen, und es sollte kein Standardwert angenommen werden. Wenn der Programmierer versehentlich keine lokale Variable initialisiert hat und den Standardwert annimmt, kann die Ausgabe ein unerwarteter Wert sein. Bei lokalen Variablen fordert der Compiler den Programmierer auf, mit einem bestimmten Wert zu initialisieren, bevor er auf die Variable zugreift, um die Verwendung undefinierter Werte zu vermeiden.
quelle
Das "Problem", auf das Sie verweisen, scheint diese Situation zu beschreiben:
Die Beschwerde des Kommentators lautet, dass der Compiler die Zeile im
finally
Abschnitt blockiert und behauptet, dass sieso
möglicherweise nicht initialisiert ist. Der Kommentar erwähnt dann eine andere Art, den Code zu schreiben, wahrscheinlich ungefähr so:Der Kommentator ist mit dieser Lösung unzufrieden, da der Compiler dann sagt, dass der Code "innerhalb eines Versuchs sein muss". Ich denke, das bedeutet, dass ein Teil des Codes eine Ausnahme auslösen kann, die nicht mehr behandelt wird. Ich bin mir nicht sicher. Keine der Versionen meines Codes behandelt Ausnahmen, daher sollte alles, was mit Ausnahmen in der ersten Version zu tun hat, in der zweiten Version gleich funktionieren.
Wie auch immer, diese zweite Version des Codes ist die richtige Art, ihn zu schreiben. In der ersten Version war die Fehlermeldung des Compilers korrekt. Die
so
Variable ist möglicherweise nicht initialisiert. Insbesondere wenn derSomeObject
Konstruktor ausfällt,so
wird er nicht initialisiert, und es ist daher ein Fehler, einen Aufruf zu versuchenso.CleanUp
. Geben Sie dentry
Abschnitt immer ein, nachdem Sie die Ressource erhalten haben, die derfinally
Abschnitt finalisiert.Der Block
try
-finally
nach derso
Initialisierung dient nur zum Schutz derSomeObject
Instanz, um sicherzustellen, dass sie bereinigt wird, unabhängig davon, was sonst noch passiert. Wenn es andere Dinge , die Notwendigkeit zu laufen, aber sie sind nicht verwandt, ob dieSomeObject
Instanz wurde Eigenschaft zugeordnet ist , dann sollten sie gehen in einen anderentry
-finally
Block, wahrscheinlich derjenige, der die eine hüllt ich gezeigt habe.Die manuelle Zuweisung von Variablen vor der Verwendung führt nicht zu echten Problemen. Es führt nur zu geringfügigen Problemen, aber Ihr Code ist besser dafür. Sie haben Variablen mit eingeschränktem Gültigkeitsbereich und
try
-finally
Blöcke, die nicht versuchen, zu viel zu schützen.Wenn lokale Variablen Standardwerte hätten,
so
wäre dies im ersten Beispiel der Fall gewesennull
. Das hätte nichts wirklich gelöst. Anstatt einen Fehler bei der Kompilierung imfinally
Block zu erhalten,NullPointerException
lauert dort ein Fehler , der möglicherweise alle anderen Ausnahmen im Abschnitt "Hier etwas arbeiten" des Codes verbirgt . (Oder verketten Ausnahmen infinally
Abschnitten automatisch mit der vorherigen Ausnahme? Ich erinnere mich nicht. Trotzdem hätten Sie eine zusätzliche Ausnahme in Bezug auf die echte.)quelle
Darüber hinaus wurde im folgenden Beispiel möglicherweise eine Ausnahme innerhalb der SomeObject-Konstruktion ausgelöst. In diesem Fall wäre die Variable 'so' null und der Aufruf von CleanUp löst eine NullPointerException aus
Was ich dazu neige, ist Folgendes:
quelle
Beachten Sie, dass die endgültigen Instanz- / Mitgliedsvariablen nicht standardmäßig initialisiert werden. Weil diese endgültig sind und danach im Programm nicht mehr geändert werden können. Aus diesem Grund gibt Java keinen Standardwert für sie an und zwingt den Programmierer, ihn zu initialisieren.
Andererseits können nicht endgültige Mitgliedsvariablen später geändert werden. Daher lässt der Compiler sie nicht uninitialisieren, gerade weil diese später geändert werden können. In Bezug auf lokale Variablen ist der Umfang lokaler Variablen viel enger. Der Compiler weiß, wann er verwendet wird. Daher ist es sinnvoll, den Programmierer zu zwingen, die Variable zu initialisieren.
quelle
Die eigentliche Antwort auf Ihre Frage lautet, dass Methodenvariablen durch einfaches Hinzufügen einer Zahl zum Stapelzeiger instanziiert werden. Sie auf Null zu setzen wäre ein zusätzlicher Schritt. Für Klassenvariablen werden sie auf dem Heap in den initialisierten Speicher gestellt.
Warum nicht den zusätzlichen Schritt machen? Machen Sie einen Schritt zurück - Niemand hat erwähnt, dass die "Warnung" in diesem Fall eine sehr gute Sache ist.
Sie sollten Ihre Variable beim ersten Durchgang (beim ersten Codieren) niemals auf Null oder Null initialisieren. Weisen Sie es entweder dem tatsächlichen Wert zu oder weisen Sie es überhaupt nicht zu, denn wenn Sie dies nicht tun, kann Java Ihnen sagen, wann Sie es wirklich vermasseln. Nehmen Sie die Antwort von Electric Monk als gutes Beispiel. Im ersten Fall ist es tatsächlich erstaunlich nützlich, dass es Ihnen sagt, dass wenn die try () fehlschlägt, weil der Konstruktor von SomeObject eine Ausnahme ausgelöst hat, Sie schließlich eine NPE erhalten würden. Wenn der Konstruktor keine Ausnahme auslösen kann, sollte dies nicht der Fall sein.
Diese Warnung ist ein großartiger Multi-Path-Bad-Programmer-Checker, der mich vor dummen Dingen bewahrt hat, da er jeden Pfad überprüft und sicherstellt, dass Sie die Variable in jedem Pfad, der zu ihr führt, initialisieren mussten, wenn Sie sie in einem Pfad verwendet haben . Ich initialisiere Variablen jetzt nie explizit, bis ich feststelle, dass dies die richtige Vorgehensweise ist.
Ist es nicht besser, explizit "int size = 0" als "int size" zu sagen und den nächsten Programmierer dazu zu bringen, herauszufinden, dass Sie beabsichtigen, Null zu sein?
Auf der anderen Seite kann ich keinen einzigen gültigen Grund finden, warum der Compiler alle nicht initialisierten Variablen auf 0 initialisiert.
quelle
Ich denke, der Hauptzweck war es, die Ähnlichkeit mit C / C ++ aufrechtzuerhalten. Der Compiler erkennt und warnt Sie jedoch vor der Verwendung nicht initialisierter Variablen, wodurch das Problem auf ein Minimum reduziert wird. Aus Sicht der Leistung ist es etwas schneller, nicht initialisierte Variablen deklarieren zu können, da der Compiler keine Zuweisungsanweisung schreiben muss, selbst wenn Sie den Wert der Variablen in der nächsten Anweisung überschreiben.
quelle
(Es mag seltsam erscheinen, so lange nach der Frage eine neue Antwort zu veröffentlichen, aber es ist ein Duplikat aufgetaucht.)
Für mich liegt der Grund darin: Der Zweck lokaler Variablen unterscheidet sich vom Zweck von Instanzvariablen. Lokale Variablen sollen im Rahmen einer Berechnung verwendet werden. Instanzvariablen sollen den Status enthalten. Wenn Sie eine lokale Variable verwenden, ohne ihr einen Wert zuzuweisen, ist dies mit ziemlicher Sicherheit ein logischer Fehler.
Das heißt, ich könnte völlig dahinter kommen, dass Instanzvariablen immer explizit initialisiert werden müssen; Der Fehler würde bei jedem Konstruktor auftreten, bei dem das Ergebnis eine nicht initialisierte Instanzvariable zulässt (z. B. bei der Deklaration nicht initialisiert und nicht im Konstruktor). Aber das ist nicht die Entscheidung von Gosling et al. al., in den frühen 90ern aufgenommen, also hier sind wir. (Und ich sage nicht, dass sie falsch angerufen haben.)
Ich konnte mich jedoch nicht hinter die standardmäßigen lokalen Variablen stellen. Ja, wir sollten uns nicht darauf verlassen, dass Compiler unsere Logik überprüfen, und das tut man auch nicht, aber es ist immer noch praktisch, wenn der Compiler eine herausfindet. :-)
quelle
Es ist effizienter, Variablen nicht zu initialisieren, und bei lokalen Variablen ist dies sicher, da die Initialisierung vom Compiler verfolgt werden kann.
In Fällen, in denen eine Variable initialisiert werden muss, können Sie dies jederzeit selbst tun, sodass dies kein Problem darstellt.
quelle
Die Idee hinter lokalen Variablen ist, dass sie nur innerhalb des begrenzten Bereichs existieren, für den sie benötigt werden. Daher sollte es wenig Grund zur Unsicherheit hinsichtlich des Werts geben oder zumindest, woher dieser Wert stammt. Ich könnte mir viele Fehler vorstellen, die sich aus einem Standardwert für lokale Variablen ergeben.
Betrachten Sie zum Beispiel den folgenden einfachen Code ... ( Hinweis: Nehmen wir zu Demonstrationszwecken an, dass lokalen Variablen ein Standardwert zugewiesen wird, wie angegeben, wenn er nicht explizit initialisiert wird. )
Wenn alles gesagt und getan ist, würde dieser geschriebene Code ordnungsgemäß funktionieren , vorausgesetzt, der Compiler hat letterGrade den Standardwert '\ 0' zugewiesen . Was aber, wenn wir die else-Anweisung vergessen haben?
Ein Testlauf unseres Codes kann Folgendes bewirken
Dieses Ergebnis war zwar zu erwarten, aber sicherlich nicht die Absicht des Programmierers. In der Tat würde der Standardwert wahrscheinlich in den allermeisten Fällen (oder zumindest in einer signifikanten Anzahl davon) nicht der gewünschte Wert sein, sodass der Standardwert in den allermeisten Fällen zu Fehlern führen würde. Es ist sinnvoller, den Codierer zu zwingen, einer lokalen Variablen vor der Verwendung einen Anfangswert zuzuweisen, da der durch das Vergessen des
= 1
In verursachte Fehler beim Debuggenfor(int i = 1; i < 10; i++)
die Bequemlichkeit überwiegt, das In nicht einschließen= 0
zu müssenfor(int i; i < 10; i++)
.Es ist wahr, dass Try-Catch-finally-Blöcke etwas chaotisch werden können (aber es ist eigentlich kein Catch-22, wie das Zitat zu vermuten scheint), wenn beispielsweise ein Objekt eine geprüfte Ausnahme in seinem Konstruktor auslöst, jedoch für eine Grund oder eine andere, muss etwas mit diesem Objekt am Ende des Blocks in endlich getan werden. Ein perfektes Beispiel hierfür ist der Umgang mit Ressourcen, die geschlossen werden müssen.
Eine Möglichkeit, in der Vergangenheit damit umzugehen, könnte so sein ...
Ab Java 7 ist dieser endgültige Block jedoch nicht mehr erforderlich, wenn Sie versuchen, Ressourcen zu verwenden.
Das heißt, (wie der Name schon sagt) funktioniert dies nur mit Ressourcen.
Und während das erstere Beispiel ein bisschen glücklich ist, spricht dies vielleicht mehr für die Art und Weise, wie try-catch-finally oder diese Klassen implementiert werden, als für lokale Variablen und wie sie implementiert werden.
Es ist wahr, dass Felder mit einem Standardwert initialisiert werden, aber das ist etwas anders. Wenn Sie beispielsweise sagen,
int[] arr = new int[10];
sobald Sie dieses Array initialisiert haben, befindet sich das Objekt an einem bestimmten Ort im Speicher. Nehmen wir für einen Moment an, dass es keine Standardwerte gibt, sondern der Anfangswert eine beliebige Reihe von Einsen und Nullen ist, die sich gerade an diesem Speicherort befindet. Dies könnte in einigen Fällen zu nicht deterministischem Verhalten führen.Angenommen, wir haben ...
Es wäre durchaus möglich, dass
Same.
dies in einem Lauf und in einem anderen LaufNot same.
angezeigt wird. Das Problem könnte noch schwerwiegender werden, wenn Sie anfangen, Referenzvariablen zu sprechen.Gemäß der Definition sollte jedes Element von s auf einen String zeigen (oder ist null). Wenn der Anfangswert jedoch eine beliebige Reihe von Nullen und Einsen ist, die an diesem Speicherort auftreten, gibt es nicht nur keine Garantie, dass Sie jedes Mal die gleichen Ergebnisse erhalten, sondern auch keine Garantie dafür, dass das Objekt s [0] zeigt zu (vorausgesetzt , es weist auf etwas Sinnvolles) selbst ist ein String (vielleicht ist es ein Kaninchen, : p )! Dieser Mangel an Sorge um den Typ würde so ziemlich allem widersprechen, was Java Java ausmacht. Während Standardwerte für lokale Variablen bestenfalls als optional angesehen werden können, sind Standardwerte für beispielsweise Variablen einer Notwendigkeit näher .
quelle
Wenn ich mich nicht irre, könnte ein anderer Grund sein
Die Angabe des Standardwerts für Elementvariablen ist Teil des Klassenladens
Das Laden von Klassen ist eine Laufzeitsache in Java. Wenn Sie ein Objekt erstellen, wird die Klasse mit dem Laden von Klassen geladen. Nur Mitgliedsvariablen werden mit dem Standardwert initialisiert. JVM benötigt keine Zeit, um Ihren lokalen Variablen einen Standardwert zuzuweisen, da einige Methoden dies niemals tun werden Wird aufgerufen, weil der Methodenaufruf bedingt sein kann. Warum sollten Sie sich also Zeit nehmen, um ihnen den Standardwert zuzuweisen und die Leistung zu verringern, wenn diese Standardeinstellungen niemals verwendet werden.
quelle
Eclipse warnt Sie sogar vor nicht initialisierten Variablen, sodass dies ohnehin ziemlich offensichtlich wird. Persönlich denke ich, dass es eine gute Sache ist, dass dies das Standardverhalten ist, andernfalls verwendet Ihre Anwendung möglicherweise unerwartete Werte, und anstatt dass der Compiler einen Fehler auslöst, wird er nichts tun (aber möglicherweise eine Warnung geben), und dann werden Sie kratzen Ihr Kopf darüber, warum sich bestimmte Dinge nicht so verhalten, wie sie sollten.
quelle
Die lokalen Variablen werden auf einem Stapel gespeichert, aber Instanzvariablen werden auf dem Heap gespeichert, sodass die Wahrscheinlichkeit groß ist, dass ein vorheriger Wert auf dem Stapel anstelle eines Standardwerts gelesen wird, wie dies im Heap der Fall ist. Aus diesem Grund erlaubt die JVM nicht, eine lokale Variable zu verwenden, ohne sie zu initialisieren.
quelle
Instanzvariable hat Standardwerte, aber die lokalen Variablen konnten keine Standardwerte haben. Da sich lokale Variablen im Wesentlichen in Methoden / Verhalten befinden, besteht ihr Hauptziel darin, einige Operationen oder Berechnungen durchzuführen. Daher ist es nicht ratsam, Standardwerte für lokale Variablen festzulegen. Andernfalls ist es sehr schwierig und zeitaufwändig, die Gründe für unerwartete Antworten zu überprüfen.
quelle
Die Antwort lautet: Instanzvariablen können im Klassenkonstruktor oder in einer beliebigen Klassenmethode initialisiert werden. Bei lokalen Variablen müssen Sie jedoch alles in der Methode definieren, was für immer in der Klasse verbleibt.
quelle
Ich könnte mir zwei Gründe vorstellen
quelle