Question Y a-t-il un destructeur pour Java?


Y a-t-il un destructeur pour Java? Je ne semble pas pouvoir trouver de documentation à ce sujet. Si ce n'est pas le cas, comment puis-je obtenir le même effet?

Pour rendre ma question plus spécifique, j'écris une application qui traite des données et la spécification dit qu'il devrait y avoir un bouton 'reset' qui ramènera l'application à son état original juste lancé. Cependant, toutes les données doivent être «actives» à moins que l'application ne soit fermée ou que le bouton de réinitialisation ne soit activé.

Étant généralement un programmeur C / C ++, je pensais que cela serait trivial à mettre en œuvre. (Et donc je prévoyais de l'implémenter en dernier.) J'ai structuré mon programme de sorte que tous les objets 'réinitialisables' soient dans la même classe afin que je puisse simplement détruire tous les objets 'vivants' quand un bouton de réinitialisation est pressé.

Je pensais que tout ce que je faisais était de déréférencer les données et d'attendre que le ramasse-miettes les récupère, n'y aurait-il pas une fuite de mémoire si mon utilisateur entrait plusieurs fois des données et pressait le bouton de réinitialisation? Je pensais aussi puisque Java est assez mature en tant que langue, il devrait y avoir un moyen d'empêcher que cela se produise ou de s'attaquer gracieusement à cela.


491
2017-10-05 13:12


origine


Réponses:


Parce que Java est un langage récupéré, vous ne pouvez pas prédire quand (ou même si) un objet sera détruit. Par conséquent, il n'y a pas d'équivalent direct d'un destructeur.

Il existe une méthode héritée appelée finalize, mais cela est appelé entièrement à la discrétion du garbage collector. Donc, pour les classes qui doivent explicitement ranger, la convention est de définir un Fermer méthode et utilisation finalise seulement pour la vérification de la santé mentale (c.-à-d. Fermer n'a pas été appelé faire maintenant et enregistrer une erreur).

Il y avait une question qui a engendré une discussion approfondie de finaliser récemment, ce qui devrait fournir plus de profondeur si nécessaire ...


453
2017-10-05 13:17



Non, pas de destructeurs ici. La raison en est que tous les objets Java sont alloués par tas et récupérés par le garbage. Sans désallocation explicite (c.-à-d. L'opérateur de suppression de C ++), il n'existe aucun moyen sensé d'implémenter de vrais destructeurs.

Java prend en charge les finaliseurs, mais ils ne sont utilisés que comme protection pour les objets contenant un handle de ressources natives telles que les sockets, les handles de fichiers, les poignées de fenêtre, etc. Lorsque le garbage collector recueille un objet sans finaliser région aussi libre et c'est tout. Lorsque l'objet a un finaliseur, il est d'abord copié dans un emplacement temporaire (souvenez-vous, nous collectons les ordures ici), puis il est placé dans une file d'attente en attente de finalisation, puis un thread Finalizer interroge la file d'attente avec une priorité très basse. et exécute le finaliseur.

Lorsque l'application se termine, la JVM s'arrête sans attendre que les objets en attente soient finalisés, il n'y a donc pratiquement aucune garantie que vos finaliseurs s'exécuteront.


102
2017-10-05 13:20



Si vous utilisez Java 7, jetez un oeil à la essayer avec des ressources déclaration. Par exemple:

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

Ici, la ressource qui n'est plus nécessaire est libérée dans le BufferedReader.close() méthode. Vous pouvez créer votre propre classe qui implémente AutoCloseableet l'utiliser de la même manière.

Cette déclaration est plus limitée que finalize en termes de structuration du code, mais en même temps, il rend le code plus simple à comprendre et à maintenir. En outre, il n'y a aucune garantie qu'un finalize La méthode est appelée du tout pendant la durée de vie de l'application.


96
2017-12-02 03:25



Utilisation de finaliser() les méthodes devraient être évitées. Ils ne sont pas un mécanisme fiable pour le nettoyage des ressources et il est possible de causer des problèmes dans le garbage collector en abusant d'eux.

Si vous avez besoin d'un appel de désallocation dans votre objet, par exemple pour libérer des ressources, utilisez un appel de méthode explicite. Cette convention peut être vue dans les API existantes (par ex. Fermable, Graphics.dispose (), Widget.dispose ()) et est généralement appelé via try / finally.

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

Les tentatives d’utilisation d’un objet éliminé doivent déclencher une exception d’exécution (voir IllegalStateException).


MODIFIER:

Je pensais, si tout ce que je faisais était juste   déréférencer les données et attendre   le ramasse-miettes pour les ramasser,   ne serait pas une fuite de mémoire si mon   l'utilisateur a saisi à plusieurs reprises des données et   appuyé sur le bouton de réinitialisation?

En règle générale, tout ce que vous devez faire est de déréférencer les objets - du moins, c'est la façon dont il est censé fonctionner. Si vous êtes préoccupé par la collecte des ordures, consultez Optimisation de la récupération de place pour la machine virtuelle Java SE 6 HotSpot [tm] (ou le document équivalent pour votre version de JVM).


22
2017-10-05 13:50



Avec Java 1.7 publié, vous avez maintenant l'option supplémentaire d'utiliser le try-with-resources bloc. Par exemple,

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..."); 
        } 
    }
}

Si vous exécutez cette classe, c.close() sera exécuté lorsque le try bloc est laissé, et avant le catch et finally les blocs sont exécutés. Contrairement au cas du finalize() méthode, close() est garanti pour être exécuté. Cependant, il n'est pas nécessaire de l'exécuter explicitement dans le finally clause.


21
2017-07-30 15:30



Je suis entièrement d'accord avec les autres réponses, en disant de ne pas compter sur l'exécution de finaliser.

En plus des blocs try-catch-finally, vous pouvez utiliser Runtime # addShutdownHook (introduit dans Java 1.6) pour effectuer des nettoyages finaux dans votre programme.

Ce n'est pas la même chose que les destructeurs sont, mais vous pouvez implémenter un hook d'arrêt ayant des objets écouteurs enregistrés sur lesquelles les méthodes de nettoyage (connexions de base de données persistantes fermantes, suppression de verrous de fichiers, etc.) peuvent être appelées - des choses qui serait normalement fait dans les destructeurs. Encore une fois - ce n'est pas un remplacement pour les constructeurs mais dans certains cas, vous pouvez aborder la fonctionnalité voulue avec ceci.

L'avantage de ceci est d'avoir un comportement de déconstructioncouplage lâche du reste de votre programme.


12
2017-10-05 14:03



Non, java.lang.Object#finalize est le plus proche possible.

Cependant, quand (et si) il est appelé, n'est pas garanti.
Voir: java.lang.Runtime#runFinalizersOnExit(boolean)


8
2017-10-05 13:14



Tout d'abord, notez que puisque Java est collecté par les ordures, il est rare de devoir faire quoi que ce soit à propos de la destruction des objets. D'abord parce que vous n'avez généralement pas de ressources gérées à libérer, et deuxièmement parce que vous ne pouvez pas prédire quand ou si cela se produira, il est donc inapproprié de faire les choses dès que personne n'utilise plus mon objet. ".

Vous pouvez être averti après qu'un objet a été détruit en utilisant java.lang.ref.PhantomReference (en fait, dire qu'il a été détruit peut être légèrement inexact, mais si une référence fantôme est mis en file d'attente, il n'est plus récupérable, ce qui revient généralement à la même chose). Un usage courant est:

  • Séparez les ressources de votre classe qui doivent être détruites dans un autre objet auxiliaire (notez que si tout ce que vous faites est de fermer une connexion, ce qui est un cas courant, vous n'avez pas besoin d'écrire une nouvelle classe: la connexion à fermer serait "l'objet auxiliaire" dans ce cas).
  • Lorsque vous créez votre objet principal, créez également un PhantomReference. Vous pouvez soit faire référence au nouvel objet auxiliaire, soit configurer une carte à partir d'objets PhantomReference à leurs objets auxiliaires correspondants.
  • Une fois l'objet principal collecté, le PhantomReference est mis en file d'attente (ou plutôt mis en file d'attente - comme les finaliseurs, il n'y a aucune garantie qu'il le sera jamais, par exemple si la machine virtuelle se ferme, elle n'attendra pas). Assurez-vous de traiter sa file d'attente (soit dans un fil spécial, soit de temps en temps). En raison de la référence à l'objet auxiliaire, l'objet auxiliaire n'a pas encore été collecté. Donc, faites le nettoyage que vous voulez sur l'objet auxiliaire, puis jetez le PhantomReference et l'assistant sera finalement collecté.

Il y a aussi finalize (), qui ressemble à un destructeur mais ne se comporte pas comme tel. Ce n'est généralement pas une bonne option.


5
2017-10-05 15:42



Je suis désolé si cela ne correspond pas au sujet principal, mais la documentation de java.util.Timer (SE6) dit:

Une fois la dernière référence à un objet Timer disparue et que toutes les tâches en attente ont été exécutées, le thread d'exécution de la tâche se termine normalement (et devient sujet à la récupération de place). Le thread d'exécution de tâches ne fonctionne pas comme un thread démon, il est donc capable d'empêcher une application de se terminer Si un appelant veut terminer rapidement le thread d'exécution d'une tâche, l'appelant doit invoquer la méthode d'annulation du timer ... "

Je voudrais appeler cancel pour que la classe possédant le Timer perd sa dernière référence (ou immeditalesky avant). Ici, un destructeur fiable pourrait le faire pour moi. Les commentaires ci-dessus indiquent que finalement c'est un mauvais choix, mais existe-t-il une solution élégante? Cette affaire de "... capable de bloquer une demande de résiliation ..." n'est pas intéressante.


5
2018-05-20 21:56