Gibt es einen Destruktor für Java?


594

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.


7
Es gibt nur einen Speicherverlust, wenn Sie Verweise auf Objekte enthalten, die Sie nicht benötigen. dh es gibt einen Fehler in Ihrem Programm. Der GC wird nach Bedarf ausgeführt (manchmal früher)
Peter Lawrey

17
Die virtuelle Maschine führt GC nicht früh genug aus, wenn Sie Daten schnell über Objekte verarbeiten. Die Idee, dass der GC immer mithalten oder die richtigen Entscheidungen treffen kann, ist ein Trugschluss.
Kieveli

1
@Kieveli Würde JVM GC nicht ausführen, bevor ein Fehler ausgegeben wird?
WVrock

4
Ja, es wäre schön, wenn es einen Destruktor für Java gäbe, der es ein für alle Mal zerstören würde.
Tomáš Zato - Wiedereinsetzung Monica

Antworten:


526

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 ...


5
Bezieht sich "close ()" in diesem Zusammenhang auf die Methode in java.lang.Autocloseable?
Sridhar Sarnobat

21
Nein, AutoCloseable wurde in Java 7 eingeführt, aber die Konvention 'close ()' gibt es schon viel länger.
Jon Onstott

warum Sie nicht vorhersagen können, wann (oder sogar ob) ein Objekt zerstört wird. Welche ungefähre andere Möglichkeit, dies vorherzusagen?
dctremblay

Die Zerstörung von @ dctremblay-Objekten erfolgt durch Garbage Collector, und Garbage Collector wird möglicherweise während der gesamten Lebensdauer der Anwendung nie ausgeführt.
Piro sagt Reinstate Monica

4
Beachten Sie, dass die finalizeMethode in Java 9 veraltet ist .
Lii

124

Schauen Sie sich die Anweisung try-with-resources an . Zum Beispiel:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

Hier wird die nicht mehr benötigte Ressource in der BufferedReader.close()Methode freigegeben . Sie können Ihre eigene Klasse erstellen, die AutoCloseablesie auf ähnliche Weise implementiert und verwendet.

Diese Aussage ist eingeschränkter als finalizein Bezug auf die Codestrukturierung, erleichtert jedoch gleichzeitig das Verständnis und die Pflege des Codes. Es gibt auch keine Garantie dafür, dass eine finalizeMethode während der Laufzeit der Anwendung überhaupt aufgerufen wird.


12
Ich bin überrascht, dass dies so wenige Stimmen hat. Es ist die eigentliche Antwort.
Nurettin

20
Ich bin nicht der Meinung, dass dies die eigentliche Antwort ist. Wenn eine Instanz über eine Ressource verfügt, die über einen längeren Zeitraum hinweg über mehrere Methodenaufrufe hinweg verarbeitet wird, hilft das Ausprobieren mit Ressourcen nicht weiter. Es sei denn, es ist in Ordnung, die Ressource mit der Geschwindigkeit zu schließen und erneut zu öffnen, mit der die Methoden aufgerufen werden - keine allgemeine Tatsache.
Eric

14
Dies ist in der Tat nicht die eigentliche Antwort. Es ist unmöglich, diese Struktur zu verwenden, um die Zerstörung eines Objekts zu verwalten, es sei denn, die Konstruktion und Verwendung des Objekts ist vollständig durch das gekapselt tryund finallywird verwendet, um einen Aufruf zu erzwingen obj.finalize(). Und selbst dieses Setup löst nicht das Problem von OP: Objektzerstörung während des Programms, ausgelöst durch eine "Reset" -Taste.
7yl4r

1
Andere Benutzer haben gezeigt, dass dies in Ihrem Anwendungseinstiegspunkt erfolgt. Definieren Sie Ihre Variable global. Initialisieren Sie es in der Eingabefunktion mit try. Deinitialisieren Sie am Ende (wenn Ihre Anwendung geschlossen wird). Es ist durchaus möglich.
TamusJRoyce

1
@nurettin Java 7 war erst seit 3 ​​Monaten nicht mehr verfügbar, als die Frage gestellt wurde, ob dies sinnvoller ist.
CorsiKa

110

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.


4
Vielen Dank, dass Sie native Ressourcen erwähnt haben. Dies ist ein Bereich, in dem eine "destruktorähnliche" Methode nützlich ist.
Nathan Osman

Ja, ich habe gerade das gleiche Problem mit der Freigabe von Ressourcen / Handles, die über native Aufrufe an C ++ zugewiesen wurden.
Nikk

@ddimitrov, könnte Java theoretisch eine explizite Freigabe implementieren? Oder ist das ein logischer Widerspruch?
Mils

1
@mils naive Implementierung einer expliziten Freigabe würde entweder die Java-Annahme brechen, dass ein Verweis auf ein lebendes Objekt verweist. Sie können alle Zeiger durchlaufen und die Aliase auf Null setzen, aber das ist teurer als GC. Oder Sie können versuchen, ein lineares Typsystem zu verwenden (siehe "Besitz" in Rust), aber das ist eine große Sprachänderung. Es gibt auch andere Optionen (siehe JavaRT-Speicher usw.), aber im Allgemeinen passt die explizite Freigabe nicht gut zur Java-Sprache.
ddimitrov

31

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.

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

Versuche, ein entsorgtes Objekt zu verwenden, sollten eine Laufzeitausnahme auslösen (siehe IllegalStateException ).


BEARBEITEN:

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?

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).


1
Das bedeutet Dereferenzierung nicht. Es geht nicht darum, "die letzte Referenz eines Objekts auf null zu setzen", sondern den Wert aus einer Referenz abzurufen (zu lesen), damit Sie ihn für nachfolgende Operationen verwenden können.

1
Ist try..finally immer noch ein gültiger und empfohlener Ansatz? Angenommen, ich habe zuvor in finalize () eine native Methode aufgerufen. Kann ich den Aufruf in die finally-Klausel verschieben? class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }
Aashima

r würde nicht auf den endgültigen Block beschränkt sein. Daher können Sie an dieser Stelle nicht zerstören nennen. Wenn Sie nun den Bereich korrigieren, in dem das Objekt vor dem try-Block erstellt werden soll, wird der hässliche Fall "vor dem Versuch mit Ressourcen" angezeigt.
UserAsh

21

Mit Java 1.7 haben Sie jetzt die zusätzliche Möglichkeit, den try-with-resourcesBlock zu verwenden. Zum Beispiel,

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

Wenn Sie diese Klasse ausführen, c.close()wird sie ausgeführt, wenn der tryBlock verlassen wird und bevor die Blöcke catchund finallyausgeführt werden. Anders als bei der finalize()Methode close()ist die Ausführung garantiert. Es ist jedoch nicht erforderlich, es explizit in der finallyKlausel auszuführen .


Was wäre, wenn wir den Try-with-Resources-Block nicht verwenden würden? Ich denke, wir können close in finalize () aufrufen, um sicherzustellen, dass close aufgerufen wurde.
ShintoZ

3
@shintoZ wie ich in den obigen Antworten gelesen habe, gibt es keine Garantie für die finalize()Ausführung
Asif Mushtaq

14

Ich 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 .


addShutdownHook wurde anscheinend in Java 1.3 eingeführt. Wie auch immer, es steht mir in 1.5 zur Verfügung. :) Siehe dies: stackoverflow.com/questions/727151/…
Skiphoppy

1
Zu Ihrer Information, meiner Erfahrung nach werden Shutdown-Hooks nicht aufgerufen, wenn Sie die rote Schaltfläche "Beenden" in Eclipse verwenden - die gesamte JVM wird sofort zerstört, Shutdown-Hooks werden nicht ordnungsgemäß aufgerufen. Das heißt, Sie können während der Entwicklung und Produktion ein anderes Verhalten
feststellen,

11

Nein, java.lang.Object#finalizeist der nächste, den Sie bekommen können.

Wann (und ob) es aufgerufen wird, ist jedoch nicht garantiert.
Sehen:java.lang.Runtime#runFinalizersOnExit(boolean)


5
Eine Methode, die aufgerufen werden kann oder nicht, ist in meinem Buch im Wesentlichen nutzlos. Es wäre besser gewesen, die Sprache nicht mit einer nutzlosen speziellen Methode zu verschmutzen, die bestenfalls ein falsches Sicherheitsgefühl vermittelt. Ich werde nie verstehen, warum die Entwickler der Java-Sprache Finalize für eine gute Idee hielten.
antred

@antred Die Entwickler der Java-Sprache sind sich einig . Ich denke, damals war es für einige von ihnen das erste Mal, dass sie eine Programmiersprache und eine Laufzeitumgebung mit Garbage Collection entwarfen. Weniger verständlich ist, warum diese andere verwaltete Sprache dieses Konzept zu einem Zeitpunkt kopierte , als bereits verstanden wurde, dass dieses Konzept eine schlechte Idee ist.
Holger

7

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:

  • Trennen Sie die Ressourcen in Ihrer Klasse, die zerstört werden müssen, in ein anderes Hilfsobjekt (beachten Sie, dass Sie keine neue Klasse schreiben müssen, wenn Sie lediglich eine Verbindung schließen, was häufig der Fall ist: Die zu schließende Verbindung wäre in diesem Fall das "Hilfsobjekt".
  • Wenn Sie Ihr Hauptobjekt erstellen, erstellen Sie auch eine Phantomreferenz darauf. Lassen Sie dies entweder auf das neue Hilfsobjekt verweisen oder richten Sie eine Zuordnung von PhantomReference-Objekten zu den entsprechenden Hilfsobjekten ein.
  • Nachdem das Hauptobjekt erfasst wurde, wird die PhantomReference in die Warteschlange gestellt (oder vielmehr in die Warteschlange gestellt - wie bei Finalisierern gibt es keine Garantie dafür, dass dies jemals der Fall sein wird. Wenn beispielsweise die VM beendet wird, wartet sie nicht). Stellen Sie sicher, dass Sie die Warteschlange verarbeiten (entweder in einem speziellen Thread oder von Zeit zu Zeit). Aufgrund des harten Verweises auf das Hilfsobjekt wurde das Hilfsobjekt noch nicht erfasst. Führen Sie also eine beliebige Bereinigung für das Hilfsobjekt durch, und verwerfen Sie dann die PhantomReference. Der Helfer wird schließlich auch gesammelt.

Es gibt auch finalize (), das wie ein Destruktor aussieht, sich aber nicht wie einer verhält. Es ist normalerweise keine gute Option.


Warum eine PhantomReferenz anstelle einer Schwachreferenz?
uckelman

2
@uckelman: Wenn Sie nur eine Benachrichtigung wünschen, erledigt PhantomReference die Aufgabe. Dies ist so ziemlich das, wofür es entwickelt wurde. Die zusätzliche Semantik von WeakReference wird hier nicht benötigt. An dem Punkt, an dem Ihre ReferenceQueue benachrichtigt wird, können Sie das Objekt nicht mehr über WeakReference wiederherstellen. Der einzige Grund für die Verwendung besteht darin, zu sparen, dass Sie sich daran erinnern müssen, dass PhantomReference vorhanden ist. Jede zusätzliche Arbeit, die WeakReference leistet, ist wahrscheinlich vernachlässigbar, aber warum sollte man sich damit beschäftigen?
Steve Jessop

Vielen Dank für den Hinweis auf PhantomReference. Es ist nicht perfekt, aber immer noch besser als nichts.
foo

@SteveJessop Welche „zusätzliche Arbeit“ hat Ihrer Meinung nach eine schwache Referenz im Vergleich zu einer Phantomreferenz?
Holger

6

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 !


5

Ich stimme den meisten Antworten zu.

Sie sollten sich nicht vollständig auf entweder finalizeoder verlassenShutdownHook

finalisieren

  1. Die JVM garantiert nicht, wann diese finalize()Methode aufgerufen wird.

  2. finalize()wird nur einmal vom GC-Thread aufgerufen. Wenn sich ein Objekt von der Finalisierungsmethode wiederbelebt, finalizewird es nicht erneut aufgerufen.

  3. In Ihrer Anwendung befinden sich möglicherweise einige Live-Objekte, für die die Speicherbereinigung niemals aufgerufen wird.

  4. Alle Exceptionvon der Finalisierungsmethode ausgelösten Daten werden vom GC-Thread ignoriert

  5. System.runFinalization(true)und Runtime.getRuntime().runFinalization(true)Methoden erhöhen die Wahrscheinlichkeit, eine finalize()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

public void addShutdownHook(Thread hook)

Registriert einen neuen Hook zum Herunterfahren der virtuellen Maschine.

Die Java Virtual Machine wird als Reaktion auf zwei Arten von Ereignissen heruntergefahren:

  1. Das Programm wird normal beendet, wenn der letzte Nicht-Daemon-Thread beendet wird oder wenn die System.exitMethode exit (äquivalent ) aufgerufen wird, oder
  2. Die virtuelle Maschine wird als Reaktion auf einen Benutzerinterrupt wie die Eingabe von ^ C oder ein systemweites Ereignis wie die Benutzerabmeldung oder das Herunterfahren des Systems beendet.
  3. Ein Shutdown-Hook ist einfach ein initialisierter, aber nicht gestarteter Thread. Wenn die virtuelle Maschine mit dem Herunterfahren beginnt, werden alle registrierten Shutdown-Hooks in einer nicht festgelegten Reihenfolge gestartet und gleichzeitig ausgeführt. Wenn alle Hooks beendet sind, werden alle nicht aufgerufenen Finalizer ausgeführt, wenn Finalization-on-Exit aktiviert wurde.
  4. Schließlich wird die virtuelle Maschine angehalten. Beachten Sie, dass Daemon-Threads während des Herunterfahrens weiterhin ausgeführt werden, ebenso wie Nicht-Daemon-Threads, wenn das Herunterfahren durch Aufrufen der Exit-Methode eingeleitet wurde.
  5. Abschalthaken 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

In seltenen Fällen kann die virtuelle Maschine abgebrochen werden, dh die Ausführung wird beendet, ohne dass das System ordnungsgemäß heruntergefahren wird

Dies tritt auf, wenn die virtuelle Maschine extern beendet wird, z. B. mit dem SIGKILLSignal unter Unix oder dem TerminateProcessAufruf 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 im finally(}Block frei. Während der Freigabe von Ressourcen im finally{}Block fangen Exceptionund fangen Throwable.


4

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.


3

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.


1
Dies ist die richtige Antwort, wenn die betreffende Ressource einen einzelnen Eigentümer hat und niemals Verweise darauf haben, die von einem anderen Code "gestohlen" wurden.
Steve Jessop

3

In Lombok gibt es eine @ Cleanup- Annotation, die hauptsächlich C ++ - Destruktoren ähnelt:

@Cleanup
ResourceClass resource = new ResourceClass();

Bei der Verarbeitung (zur Kompilierungszeit) fügt Lombok den entsprechenden try-finallyBlock ein, damit er resource.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, z resource.dispose().

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();

2
Der Vorteil, den ich sehe, ist, dass es weniger Verschachtelungen gibt (was bedeutsam sein kann, wenn Sie viele Objekte haben, die "zerstört" werden müssen).
Alexey

Ein Try-with-Resource-Block kann mehrere Ressourcen auf einmal enthalten
Alexander - Reinstate Monica

1
Aber es kann keine Anweisungen zwischen ihnen haben.
Alexey

Messe. Ich nehme an, dann wird empfohlen, Try-with-Resource auch für mehrere Ressourcen zu bevorzugen, es sei denn, es musste eine Anweisung zwischen ihnen vorhanden sein, die Sie dazu zwang, neue Try-with-Resource-Blöcke zu erstellen (zunehmende Verschachtelung), und dann@Cleanup
Alexander - Reinstate verwenden Monica

2

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.


2

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 wird Runtime.getRuntime().exit(), werden Finalizer nicht standardmäßig ausgeführt. Von Javadoc für Runtime.exit () :

Die Abschaltsequenz der virtuellen Maschine besteht aus zwei Phasen. In der ersten Phase werden alle registrierten Shutdown-Hooks, falls vorhanden, in einer nicht festgelegten Reihenfolge gestartet und können bis zu ihrem Abschluss gleichzeitig ausgeführt werden. In der zweiten Phase werden alle nicht aufgerufenen Finalizer ausgeführt, wenn die Finalisierung beim Beenden aktiviert wurde. Sobald dies erledigt ist, wird die virtuelle Maschine angehalten.

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.


1

Wenn Sie ein Java-Applet schreiben, können Sie die Applet-Methode "destroy ()" überschreiben. Es ist...

 * Called by the browser or applet viewer to inform
 * this applet that it is being reclaimed and that it should destroy
 * any resources that it has allocated. The stop() method
 * will always be called before destroy().

Offensichtlich nicht das, was Sie wollen, aber vielleicht das, wonach andere Leute suchen.


1

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 eine closeMethode hat), können Sie es Bagbeim 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 das CloseablesSchließen sie ...?

Der Code würde wahrscheinlich ungefähr so ​​aussehen:

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseablesDies wäre wahrscheinlich eine Blockierungsmethode, die wahrscheinlich einen Latch (z. B. CountdownLatch) umfasst, um alle Runnables/ Callablesin Threads zu behandeln (und gegebenenfalls darauf zu warten) Playpen, insbesondere die im JavaFX-Thread.


0

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.



0

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.


-3

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:

public myDestructor() {

variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL

}

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.


Versuchen Sie, unveränderliche Objekte mehr zu verwenden, nachdem Sie es "bekommen", wird alles sinnvoller sein :)
R. van Twisk

5
Das ist nicht so sehr falsch, sondern sinnlos - dh es wird nichts erreicht. Wenn für Ihr Programm das Zurücksetzen von Rohtypen erforderlich ist, um ordnungsgemäß zu funktionieren, haben Ihre Klasseninstanzen einen falschen Gültigkeitsbereich. Dies bedeutet, dass Sie wahrscheinlich die Eigenschaften eines vorhandenen Objekts den Eigenschaften eines neuen Objekts neu zuweisen, ohne ein neues Objekt zu erstellen ().
simo.3792

1
Abgesehen davon, dass viele Variablen zurückgesetzt werden müssen. Ich habe den Namen Destruktor gewählt, weil er zu dem passt, was ich tue. Es erreicht etwas, nur nichts, was Sie verstehen
ChristianGLong
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.