Gibt es einen Destruktor für Java? Ich scheine keine Dokumentation dazu zu finden. Wenn nicht, wie kann ich den gleichen Effekt erzielen?
Um meine Frage genauer zu formulieren, schreibe ich eine Anwendung, die sich mit Daten befasst, und die Spezifikation besagt, dass es eine Schaltfläche zum Zurücksetzen geben sollte, die die Anwendung wieder in ihren ursprünglichen, gerade gestarteten Zustand versetzt. Alle Daten müssen jedoch "live" sein, es sei denn, die Anwendung wird geschlossen oder die Reset-Taste gedrückt.
Da ich normalerweise ein C / C ++ - Programmierer bin, dachte ich, dass dies trivial zu implementieren wäre. (Und daher hatte ich vor, es zuletzt zu implementieren.) Ich habe mein Programm so strukturiert, dass sich alle 'zurücksetzbaren' Objekte in derselben Klasse befinden, sodass ich nur alle 'lebenden' Objekte zerstören kann, wenn eine Reset-Taste gedrückt wird.
Ich dachte, wenn ich nur die Daten dereferenzieren und darauf warten würde, dass der Garbage Collector sie sammelt, würde es dann keinen Speicherverlust geben, wenn mein Benutzer wiederholt Daten eingibt und die Reset-Taste drückt? Ich dachte auch, da Java als Sprache ziemlich ausgereift ist, sollte es eine Möglichkeit geben, dies zu verhindern oder dies anmutig anzugehen.
Antworten:
Da Java eine durch Müll gesammelte Sprache ist, können Sie nicht vorhersagen, wann (oder sogar ob) ein Objekt zerstört wird. Daher gibt es kein direktes Äquivalent eines Destruktors.
Es wird eine geerbte Methode aufgerufen
finalize
, die jedoch vollständig im Ermessen des Garbage Collectors liegt. Für Klassen, die explizit aufgeräumt werden müssen, besteht die Konvention darin, eine Abschlussmethode zu definieren und finalize nur für die Überprüfung der Integrität zu verwenden (dh wenn close nicht aufgerufen wurde, tun Sie dies jetzt und protokollieren Sie einen Fehler).Es gab eine Frage, die kürzlich zu einer eingehenden Diskussion über die Fertigstellung geführt hat, sodass bei Bedarf mehr Tiefe bereitgestellt werden sollte ...
quelle
finalize
Methode in Java 9 veraltet ist .Schauen Sie sich die Anweisung try-with-resources an . Zum Beispiel:
Hier wird die nicht mehr benötigte Ressource in der
BufferedReader.close()
Methode freigegeben . Sie können Ihre eigene Klasse erstellen, dieAutoCloseable
sie auf ähnliche Weise implementiert und verwendet.Diese Aussage ist eingeschränkter als
finalize
in Bezug auf die Codestrukturierung, erleichtert jedoch gleichzeitig das Verständnis und die Pflege des Codes. Es gibt auch keine Garantie dafür, dass einefinalize
Methode während der Laufzeit der Anwendung überhaupt aufgerufen wird.quelle
try
undfinally
wird verwendet, um einen Aufruf zu erzwingenobj.finalize()
. Und selbst dieses Setup löst nicht das Problem von OP: Objektzerstörung während des Programms, ausgelöst durch eine "Reset" -Taste.Nein, keine Zerstörer hier. Der Grund dafür ist, dass alle Java-Objekte Heap-zugeordnet und Müll gesammelt werden. Ohne explizite Freigabe (dh den Löschoperator von C ++) gibt es keine sinnvolle Möglichkeit, echte Destruktoren zu implementieren.
Java unterstützt zwar Finalizer, sie dienen jedoch nur als Schutz für Objekte, die ein Handle für native Ressourcen wie Sockets, Dateihandles, Fensterhandles usw. enthalten. Wenn der Garbage Collector ein Objekt ohne Finalizer sammelt, markiert er einfach den Speicher Region als frei und das wars. Wenn das Objekt einen Finalizer hat, wird es zuerst an einen temporären Speicherort kopiert (denken Sie daran, wir sammeln hier Müll), dann in eine Warteschlange, die darauf wartet, finalisiert zu werden, und dann fragt ein Finalizer-Thread die Warteschlange mit sehr niedriger Priorität ab und führt den Finalizer aus.
Wenn die Anwendung beendet wird, stoppt die JVM, ohne auf die Finalisierung der ausstehenden Objekte zu warten. Daher gibt es praktisch keine Garantie dafür, dass Ihre Finalizer jemals ausgeführt werden.
quelle
Verwendung von finalize () -Methoden sollte vermieden werden. Sie sind kein zuverlässiger Mechanismus für die Bereinigung von Ressourcen, und es ist möglich, Probleme im Garbage Collector zu verursachen, indem sie missbraucht werden.
Wenn Sie einen Freigabeaufruf in Ihrem Objekt benötigen, um beispielsweise Ressourcen freizugeben, verwenden Sie einen expliziten Methodenaufruf. Diese Konvention kann in vorhandenen APIs (z. B. Closeable , Graphics.dispose () , Widget.dispose () ) verwendet werden und wird normalerweise über try / finally aufgerufen.
Versuche, ein entsorgtes Objekt zu verwenden, sollten eine Laufzeitausnahme auslösen (siehe IllegalStateException ).
BEARBEITEN:
Im Allgemeinen müssen Sie nur die Objekte dereferenzieren - zumindest soll dies so funktionieren. Wenn Sie sich Sorgen über die Speicherbereinigung machen, lesen Sie die Optimierung der Speicherbereinigung für Java SE 6 HotSpot [tm] Virtual Machine (oder das entsprechende Dokument für Ihre JVM-Version).
quelle
class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }
Mit Java 1.7 haben Sie jetzt die zusätzliche Möglichkeit, den
try-with-resources
Block zu verwenden. Zum Beispiel,Wenn Sie diese Klasse ausführen,
c.close()
wird sie ausgeführt, wenn dertry
Block verlassen wird und bevor die Blöckecatch
undfinally
ausgeführt werden. Anders als bei derfinalize()
Methodeclose()
ist die Ausführung garantiert. Es ist jedoch nicht erforderlich, es explizit in derfinally
Klausel auszuführen .quelle
finalize()
AusführungIch stimme anderen Antworten voll und ganz zu und sage, dass ich mich nicht auf die Ausführung von finalize verlassen soll.
Zusätzlich zu Try-Catch-finally-Blöcken können Sie Runtime # addShutdownHook verwenden (eingeführt in Java 1.3) verwenden, um endgültige Bereinigungen in Ihrem Programm durchzuführen.
Das ist nicht dasselbe wie bei Destruktoren , aber man kann einen Shutdown-Hook implementieren, bei dem Listener-Objekte registriert sind, für die Bereinigungsmethoden (Schließen persistenter Datenbankverbindungen, Entfernen von Dateisperren usw.) aufgerufen werden können - Dinge, die normalerweise ausgeführt werden Zerstörer . Auch dies ist kein Ersatz für Destruktoren, aber in einigen Fällen können Sie damit die gewünschte Funktionalität erreichen.
Dies hat den Vorteil, dass das Dekonstruktionsverhalten lose mit dem Rest Ihres Programms gekoppelt ist .
quelle
Nein,
java.lang.Object#finalize
ist der nächste, den Sie bekommen können.Wann (und ob) es aufgerufen wird, ist jedoch nicht garantiert.
Sehen:
java.lang.Runtime#runFinalizersOnExit(boolean)
quelle
Beachten Sie zunächst, dass Java, da es sich um Müll handelt, selten etwas gegen die Zerstörung von Objekten unternehmen muss. Erstens, weil Sie normalerweise keine verwalteten Ressourcen zum Freigeben haben, und zweitens, weil Sie nicht vorhersagen können, wann oder ob dies geschehen wird. Daher ist es für Dinge, die auftreten müssen, unangemessen, "sobald niemand mehr mein Objekt verwendet." ".
Sie können benachrichtigt werden, nachdem ein Objekt mit java.lang.ref.PhantomReference zerstört wurde (tatsächlich kann es leicht ungenau sein, zu sagen, dass es zerstört wurde, aber wenn ein Phantomverweis darauf in die Warteschlange gestellt wird, kann es nicht mehr wiederhergestellt werden, was normalerweise gleich ist das gleiche). Eine häufige Verwendung ist:
Es gibt auch finalize (), das wie ein Destruktor aussieht, sich aber nicht wie einer verhält. Es ist normalerweise keine gute Option.
quelle
Die
finalize()
Funktion ist der Destruktor.Es sollte jedoch normalerweise nicht verwendet werden, da es nach dem GC aufgerufen wird und Sie nicht sagen können, wann dies geschehen wird (falls jemals).
Darüber hinaus ist mehr als ein GC erforderlich, um die Zuordnung von Objekten aufzuheben
finalize()
.Sie sollten versuchen, mit den
try{...} finally{...}
Anweisungen an den logischen Stellen in Ihrem Code aufzuräumen !quelle
Ich stimme den meisten Antworten zu.
Sie sollten sich nicht vollständig auf entweder
finalize
oder verlassenShutdownHook
finalisieren
Die JVM garantiert nicht, wann diese
finalize()
Methode aufgerufen wird.finalize()
wird nur einmal vom GC-Thread aufgerufen. Wenn sich ein Objekt von der Finalisierungsmethode wiederbelebt,finalize
wird es nicht erneut aufgerufen.In Ihrer Anwendung befinden sich möglicherweise einige Live-Objekte, für die die Speicherbereinigung niemals aufgerufen wird.
Alle
Exception
von der Finalisierungsmethode ausgelösten Daten werden vom GC-Thread ignoriertSystem.runFinalization(true)
undRuntime.getRuntime().runFinalization(true)
Methoden erhöhen die Wahrscheinlichkeit, einefinalize()
Methode aufzurufen , aber jetzt sind diese beiden Methoden veraltet. Diese Methoden sind aufgrund mangelnder Thread-Sicherheit und möglicher Deadlock-Erstellung sehr gefährlich.shutdownHooks
Die Java Virtual Machine wird als Reaktion auf zwei Arten von Ereignissen heruntergefahren:
System.exit
Methode exit (äquivalent ) aufgerufen wird, oderAbschalthaken sollten auch ihre Arbeit schnell beenden. Wenn ein Programm exit aufruft, wird erwartet, dass die virtuelle Maschine sofort heruntergefahren und beendet wird.
Aber selbst die Oracle-Dokumentation hat das zitiert
Dies tritt auf, wenn die virtuelle Maschine extern beendet wird, z. B. mit dem
SIGKILL
Signal unter Unix oder demTerminateProcess
Aufruf unter Microsoft Windows. Die virtuelle Maschine kann auch abgebrochen werden, wenn eine native Methode schief geht, indem beispielsweise interne Datenstrukturen beschädigt werden oder versucht wird, auf nicht vorhandenen Speicher zuzugreifen. Wenn die virtuelle Maschine abgebrochen wird, kann nicht garantiert werden, ob Shutdown-Hooks ausgeführt werden.Fazit : Verwenden Sie
try{} catch{} finally{}
Blöcke entsprechend und geben Sie kritische Ressourcen imfinally(}
Block frei. Während der Freigabe von Ressourcen imfinally{}
Block fangenException
und fangenThrowable
.quelle
Wenn es nur um die Erinnerung geht, um die Sie sich Sorgen machen, tun Sie es nicht. Vertraue einfach dem GC, dass er einen anständigen Job macht. Ich habe tatsächlich gesehen, dass etwas so effizient ist, dass es für die Leistung besser sein könnte, Haufen winziger Objekte zu erstellen, als in einigen Fällen große Arrays zu verwenden.
quelle
Vielleicht können Sie einen try ... finally-Block verwenden, um das Objekt in dem Kontrollfluss zu finalisieren, in dem Sie das Objekt verwenden. Natürlich geschieht dies nicht automatisch, aber auch nicht die Zerstörung in C ++. Sie sehen oft das Schließen von Ressourcen im finally-Block.
quelle
In Lombok gibt es eine @ Cleanup- Annotation, die hauptsächlich C ++ - Destruktoren ähnelt:
Bei der Verarbeitung (zur Kompilierungszeit) fügt Lombok den entsprechenden
try-finally
Block ein, damit erresource.close()
aufgerufen wird, wenn die Ausführung den Bereich der Variablen verlässt. Sie können auch explizit eine andere Methode zum Freigeben der Ressource angeben, zresource.dispose()
.quelle
@Cleanup
Das nächste Äquivalent zu einem Destruktor in Java ist die finalize () -Methode. Der große Unterschied zu einem herkömmlichen Destruktor besteht darin, dass Sie nicht sicher sind, wann er aufgerufen wird, da dies in der Verantwortung des Garbage Collectors liegt. Ich würde dringend empfehlen, dies sorgfältig zu lesen, bevor Sie es verwenden, da Ihre typischen RAIA-Muster für Dateihandles usw. mit finalize () nicht zuverlässig funktionieren.
quelle
Viele gute Antworten hier, aber es gibt einige zusätzliche Informationen darüber, warum Sie die Verwendung von finalize () vermeiden sollten. .
Wenn die JVM aufgrund von
System.exit()
oder beendet wirdRuntime.getRuntime().exit()
, werden Finalizer nicht standardmäßig ausgeführt. Von Javadoc für Runtime.exit () :Du kannst anrufen
System.runFinalization()
aber es ist nur "eine beste Anstrengung, alle ausstehenden Finalisierungen abzuschließen" - keine Garantie.Es gibt eine
System.runFinalizersOnExit()
Methode, aber verwenden Sie sie nicht - sie ist unsicher und längst veraltet.quelle
Wenn Sie ein Java-Applet schreiben, können Sie die Applet-Methode "destroy ()" überschreiben. Es ist...
Offensichtlich nicht das, was Sie wollen, aber vielleicht das, wonach andere Leute suchen.
quelle
Wenn wir nur an die ursprüngliche Frage denken ... die wir aus all den anderen gelernten Antworten und auch aus Blochs essentiellem effektivem Java , Punkt 7, "Vermeiden Sie Finalisierer", schließen können, sucht die Lösung für eine legitime Frage auf eine Weise, die ist für die Java-Sprache unangemessen ...:
... wäre es keine ziemlich offensichtliche Lösung, das zu tun, was das OP eigentlich will, alle Ihre Objekte, die zurückgesetzt werden müssen, in einer Art "Laufstall" zu halten, auf den alle anderen nicht zurücksetzbaren Objekte nur durch irgendeine Art von Verweisen verweisen des Accessor-Objekts ...
Und wenn Sie dann "zurücksetzen" müssen, trennen Sie den vorhandenen Laufstall und erstellen einen neuen: Das gesamte Netz der Objekte im Laufstall wird abgetrieben, um niemals zurückzukehren und eines Tages vom GC gesammelt zu werden.
Wenn eines dieser Objekte vorhanden ist
Closeable
(oder nicht, aber eineclose
Methode hat), können Sie esBag
beim Erstellen (und möglicherweise Öffnen) in den Laufstall einfügen. Der letzte Vorgang des Accessors vor dem Abschneiden des Laufstalls besteht darin, zu gehen durch all dasCloseables
Schließen sie ...?Der Code würde wahrscheinlich ungefähr so aussehen:
closeCloseables
Dies wäre wahrscheinlich eine Blockierungsmethode, die wahrscheinlich einen Latch (z. B.CountdownLatch
) umfasst, um alleRunnables
/Callables
in Threads zu behandeln (und gegebenenfalls darauf zu warten)Playpen
, insbesondere die im JavaFX-Thread.quelle
Obwohl die GC-Technologie von Java erheblich weiterentwickelt wurde, müssen Sie Ihre Referenzen berücksichtigen. Zahlreiche Fälle von scheinbar trivialen Referenzmustern, bei denen es sich tatsächlich um Rattennester unter der Haube handelt, kommen in den Sinn.
Aus Ihrem Beitrag geht nicht hervor, dass Sie versuchen, eine Rücksetzmethode zum Zwecke der Wiederverwendung von Objekten zu implementieren (true?). Enthalten Ihre Objekte andere Arten von Ressourcen, die bereinigt werden müssen (z. B. Streams, die geschlossen werden müssen, gepoolte oder geliehene Objekte, die zurückgegeben werden müssen)? Wenn das einzige, worüber Sie sich Sorgen machen, die Freigabe des Speichers ist, würde ich meine Objektstruktur überdenken und versuchen zu überprüfen, ob meine Objekte in sich geschlossene Strukturen sind, die zur GC-Zeit bereinigt werden.
quelle
In Java gibt es keine exakte Destruktorklasse, die in Java vom Garbage Collector automatisch zerstört wird. Aber Sie könnten das mit einem tun, aber es ist nicht genau dasselbe:
finalize ()
Es gab eine Frage, die zu einer eingehenden Diskussion über das Finalisieren führte , sodass Sie bei Bedarf mehr Tiefe erhalten sollten ...
quelle
Kein Java hat keine Destruktoren. Der Hauptgrund dafür in Java sind die Garbage Collectors, die immer passiv im Hintergrund arbeiten und alle Objekte werden im Heap-Speicher erstellt. Dies ist der Ort, an dem GC funktioniert. In C ++ gibt es sie müssen die Löschfunktion explizit aufrufen, da es kein Garbage Collector-ähnliches Ding gibt.
quelle
Früher habe ich mich hauptsächlich mit C ++ beschäftigt und das hat mich auch zur Suche nach einem Destruktor geführt. Ich benutze JAVA jetzt viel. Was ich getan habe, und es ist vielleicht nicht der beste Fall für alle, aber ich habe meinen eigenen Destruktor implementiert, indem ich alle Werte entweder auf 0 oder auf den Standardwert über eine Funktion zurückgesetzt habe.
Beispiel:
Im Idealfall funktioniert dies nicht in allen Situationen, aber wenn globale Variablen vorhanden sind, funktioniert dies, solange Sie nicht über eine Menge davon verfügen.
Ich weiß, dass ich nicht der beste Java-Programmierer bin, aber es scheint für mich zu funktionieren.
quelle