Ich habe eine einfache Java-Klasse wie unten gezeigt:
public class Test {
private String s;
public String foo() {
try {
s = "dev";
return s;
}
finally {
s = "override variable s";
System.out.println("Entry in finally Block");
}
}
public static void main(String[] xyz) {
Test obj = new Test();
System.out.println(obj.foo());
}
}
Und die Ausgabe dieses Codes ist folgende:
Entry in finally Block
dev
Warum wird s
im finally
Block nicht überschrieben , aber die gedruckte Ausgabe gesteuert?
java
try-finally
Dev
quelle
quelle
s
bevor Sie den Wert ändern.finally
Block zurückgeben , im Gegensatz zu C # (was Sie nicht können)Antworten:
Der
try
Block wird mit der Ausführung derreturn
Anweisung abgeschlossen, und der Werts
zum Zeitpunkt derreturn
Ausführung der Anweisung ist der von der Methode zurückgegebene Wert. Die Tatsache, dass diefinally
Klausel später den Wert vons
(nach Abschluss derreturn
Anweisung) ändert, ändert (zu diesem Zeitpunkt) den Rückgabewert nicht.Beachten Sie, dass sich das Obige auf Änderungen des Werts von sich
s
selbst imfinally
Blocks
bezieht , nicht auf das Objekt, auf das verwiesen wird. Wenns
ein Verweis auf ein veränderbares Objekt (wasString
nicht der Fall ist) und der Inhalt des Objekts imfinally
Block geändert wurden , werden diese Änderungen im zurückgegebenen Wert angezeigt.Die detaillierten Regeln für die Funktionsweise finden Sie in Abschnitt 14.20.2 der Java-Sprachspezifikation . Beachten Sie, dass die Ausführung einer
return
Anweisung als abrupte Beendigung destry
Blocks gilt (es gilt der Abschnitt " Wenn die Ausführung des try-Blocks aus einem anderen Grund abrupt abgeschlossen wird ... "). In Abschnitt 14.17 des JLS erfahren Sie, warum einereturn
Anweisung eine abrupte Beendigung eines Blocks darstellt.Zur weiteren Detaillierung: Wenn sowohl der
try
Block als auch derfinally
Block einertry-finally
Anweisung aufgrund vonreturn
Anweisungen abrupt beendet werden , gelten die folgenden Regeln aus §14.20.2:Das Ergebnis ist, dass die
return
Anweisung imfinally
Block den Rückgabewert der gesamtentry-finally
Anweisung bestimmt und der vomtry
Block zurückgegebene Wert verworfen wird. Ähnliches gilt für einetry-catch-finally
Anweisung, wenn dertry
Block eine Ausnahme auslöst, von einemcatch
Block abgefangen wird und sowohl dercatch
Block als auch derfinally
Blockreturn
Anweisungen haben.quelle
finally
ändert der Block nicht, welches Objekt zurückgegeben wird,StringBuilder
sondern die Interna des Objekts. Derfinally
Block wird ausgeführt, bevor die Methode tatsächlich zurückgegeben wird (obwohl diereturn
Anweisung beendet wurde), sodass diese Änderungen auftreten, bevor der aufrufende Code den zurückgegebenen Wert sieht.StringBuilder
,List
,Set
, bis zum Überdruss): Wenn Sie den Inhalt im ändernfinally
Block, dann werden diese Änderungen in dem Aufruf - Code zu sehen , wenn das Verfahren schließlich beendet.Weil der Rückgabewert vor dem Aufruf von finally auf den Stack gelegt wird.
quelle
=
würde using ihn nicht mutieren.finally
Block von OP den Rückgabewert nicht beeinflusst hat. Ich denke, was templatetypedef möglicherweise erreicht hat (obwohl dies nicht klar ist), ist, dass selbst das Ändern des Codes imfinally
Block (außer die Verwendung einer anderenreturn
Anweisung) keine Auswirkungen auf das hat , da der zurückgegebene Wert eine Referenz auf ein unveränderliches Objekt ist Von der Methode zurückgegebener Wert.Wenn wir uns den Bytecode ansehen, werden wir feststellen, dass JDK eine signifikante Optimierung vorgenommen hat und die foo () -Methode wie folgt aussieht:
Und Bytecode:
Java hat verhindert, dass der "dev" -String vor der Rückkehr geändert wird. Tatsächlich gibt es hier überhaupt keinen endgültigen Block.
quelle
Hier sind zwei Dinge bemerkenswert:
quelle
finally
Klausel ändern würden, würde dies im aufrufenden Code angezeigt . Wenn Sie jedoch einen neuen Zeichenfolgenpuffer zugewiesen habens
, ist das Verhalten das gleiche wie jetzt.Ich ändere deinen Code ein wenig, um den Sinn von Ted zu beweisen.
Wie Sie in der Ausgabe sehen können,
s
wird zwar geändert, aber nach der Rückkehr.Ausgabe:
quelle
Technisch gesehen wird der
return
Block im try-Block nicht ignoriert, wenn einfinally
Block definiert ist, sondern nur, wenn dieser endgültige Block auch a enthältreturn
.Es ist eine zweifelhafte Entwurfsentscheidung, die im Nachhinein wahrscheinlich ein Fehler war (ähnlich wie Referenzen, die standardmäßig nullbar / veränderbar sind, und nach einigen überprüften Ausnahmen). In vielerlei Hinsicht stimmt dieses Verhalten genau mit dem umgangssprachlichen Verständnis dessen überein, was
finally
bedeutet: "Egal, was vorher imtry
Block passiert , führen Sie diesen Code immer aus." Wenn Sie also von einemfinally
Block true zurückgeben , muss der Gesamteffekt immer zu seinreturn s
, nein?Im Allgemeinen ist dies selten eine gute Redewendung, und Sie sollten
finally
Blöcke großzügig zum Bereinigen / Schließen von Ressourcen verwenden, aber selten oder nie einen Wert von ihnen zurückgeben.quelle
Versuchen Sie Folgendes: Wenn Sie den Überschreibungswert von s drucken möchten.
quelle