web-dev-qa-db-fra.com

Java dernier modificateur

On m'a dit que je comprenais mal les effets de final. Quels sont les effets du mot clé final?

Voici un bref aperçu de ce que je pense, je sais:

Modificateur final Java (ou relation d'agrégation)

variables primitives: ne peut être défini qu'une seule fois. (gain de mémoire et de performances)
variables d'objets: peut être modifié, le final s'applique à la référence d'objet.
champs: ne peut être défini qu'une seule fois.
méthodes: ne peut pas être remplacé, masqué.
classes: ne peut pas être étendu.
garbage collection: forcera Java génération de garbage collection mark-sweep à double sweep.

Can's et Cant's

  • Peut faire échouer le clone (c'est à la fois bon et mauvais)
  • Peut faire des primitives immuables aka const
  • Peut rendre le blanc immuable - initialisé à la création, alias en lecture seule
  • Peut rendre des objets peu immuables
  • Peut rendre l'étendue/la visibilité immuable
  • Peut réduire le surdébit d'appel de méthode (car il n'a pas besoin de table virtuelle)
  • Peut faire des arguments de méthode utilisés comme final (même si ce n'est pas le cas)
  • Peut rendre les objets threadsafe (si l'objet est défini comme final, il ne rendra pas les arguments de méthode définitifs)
  • Peut faire des tests simulés (pas que vous puissiez faire quoi que ce soit à ce sujet - vous pouvez dire que les bogues sont destinés)
  • Impossible de se faire des amis (mutable avec d'autres amis et immuable pour le repos)
  • Impossible de rendre mutable qui est modifié pour être immuable plus tard (mais peut avec un modèle d'usine comme un correctif)
  • Impossible de rendre les éléments du tableau immuables ou profondément immuables
  • Impossible de créer de nouvelles instances d'objet (c'est à la fois bon et mauvais)
  • Impossible de faire fonctionner la sérialisation

Il n'y a pas d'alternative à final, mais il y a wrapper + private et enums.

54
Margus

Répondre à tour de rôle à chacun de vos points:

variables primitives: ne peuvent être définies qu'une seule fois. (gain de mémoire et de performances)

Oui, mais aucun gain de mémoire et aucun gain de performances. (Votre gain de performances supposé provient de la définition d'une seule fois ... pas de final.)

variables d'objets: peuvent être modifiées, la finale s'applique à la référence d'objet.

Oui. (Cependant, cette description ne tient pas compte du fait que cela est entièrement cohérent avec la façon dont le reste du langage Java traite la dualité objet/référence. Par exemple, lorsque des objets sont passés en tant que paramètres et retourné comme résultat.)

champs: ne peuvent être définis qu'une seule fois.

La vraie réponse est la même que pour les variables.

méthodes: ne peuvent pas être remplacées, masquées.

Oui. Mais notez également que ce qui se passe ici est que le mot clé final est utilisé dans un contexte syntaxique différent pour signifier quelque chose de différent de final pour un champ/une variable.

classes: non extensible.

Oui. Mais voir également la note ci-dessus.

garbage collection: forcera Java génération-garbage collection mark-sweep à double balayage.

Ça n'a pas de sens. Le mot clé final n'a aucune pertinence que ce soit pour la récupération de place. Vous pouvez confondre final avec la finalisation ... ils ne sont pas liés.

Mais même les finaliseurs ne forcent pas un balayage supplémentaire. Ce qui se passe, c'est qu'un objet qui doit être finalisé est placé sur un côté jusqu'à la fin du GC principal. Le GC exécute ensuite la méthode finalize sur l'objet et définit son indicateur ... et continue. La prochaine fois que le GC s'exécute, l'objet est traité comme un objet normal:

  • s'il est accessible, il est marqué et copié
  • s'il n'est pas accessible, il n'est pas marqué.

(Votre caractérisation - "Java générationnel garbage collection mark-sweep" est tronquée. Un garbage collector peut être soit "mark-sweep" OR "generational" (une sous-classe de "copying"). Il ne peut pas être les deux. Java utilise normalement la collection générationnelle, et ne retombe que pour balayer les marques en cas d'urgence, c'est-à-dire lorsqu'il manque d'espace ou lorsqu'un collecteur à faible pause ne peut pas suivre.)

Peut faire échouer le clone (c'est à la fois bon et mauvais)

Je ne pense pas.

Peut faire des primitives immuables aka const

Oui.

Peut rendre le blanc immuable - initialisé à la création, alias en lecture seule

Oui ... même si je n'ai jamais entendu le terme "vierge immuable" utilisé auparavant.

Peut rendre des objets peu immuables

La mutabilité des objets consiste à savoir si l'état observable peut changer. En tant que tel, la déclaration des attributs final peut ou non faire en sorte que l'objet se comporte comme immuable. En outre, la notion de "superficiellement immuable" n'est pas bien définie, notamment parce que la notion de "superficiel" ne peut pas être cartographiée sans une connaissance approfondie de la sémantique des classes.

(Pour être clair, la mutabilité des variables/champs est un concept bien défini dans le contexte du JLS. C'est juste le concept de mutabilité des objets qui n'est pas défini du point de vue du JLS.)

Peut rendre l'étendue/la visibilité immuable

Erreur de terminologie. La mutabilité concerne l'état de l'objet. La visibilité et la portée ne le sont pas.

Peut réduire le surdébit d'appel de méthode (car il n'a pas besoin de table virtuelle)

En pratique, cela n'est pas pertinent. Un compilateur JIT moderne effectue également cette optimisation pour les méthodes non finales, si elles ne sont remplacées par aucune classe que l'application utilise réellement. (Des trucs intelligents se produisent ...)

Peut faire des arguments de méthode utilisés comme final (même si ce n'est pas le cas)

Hein? Je ne peux pas analyser cette phrase.

Peut rendre les objets threadsafe

Dans certaines situations, oui.

(si l'objet est défini comme final, il ne rendra pas les arguments de méthode définitifs)

Oui, si vous voulez dire si class est final. Les objets ne sont pas définitifs.

Peut faire des tests simulés (pas que vous puissiez faire quoi que ce soit à ce sujet - vous pouvez dire que les bogues sont destinés)

N'analyse pas.

Impossible de se faire des amis (mutable avec d'autres amis et immuable pour le repos)

Java n'a pas "d'amis".

Impossible de rendre mutable qui est modifié pour être immuable plus tard (mais peut avec un modèle d'usine comme un correctif)

Oui au premier, un champ final ne peut pas passer de mutable à immuable.

On ne sait pas ce que vous entendez par la deuxième partie. Il est vrai que vous pouvez utiliser un modèle d'usine (ou de générateur) pour construire des objets immuables. Cependant, si vous utilisez final pour les champs d'objet à aucun moment, l'objet ne peut être modifié.

Alternativement, vous pouvez implémenter des objets immuables qui utilisent des champs non finaux pour représenter un état immuable, et vous pouvez concevoir l'API de sorte que vous puissiez "basculer un commutateur" pour rendre un objet précédemment mutable immuable à partir de maintenant. Mais si vous adoptez cette approche, vous devez être beaucoup plus prudent avec la synchronisation ... si vos objets doivent être thread-safe.

Impossible de rendre les éléments du tableau immuables ou profondément immuables

Oui, mais votre terminologie est brisée; voir le commentaire ci-dessus sur la "mutabilité superficielle".

Impossible de créer de nouvelles instances d'objet (c'est à la fois bon et mauvais)

Non. Rien ne vous empêche de créer une nouvelle instance d'un objet avec des champs finaux ou une classe finale ou des méthodes finales.

Impossible de faire fonctionner la sérialisation

Non. La sérialisation fonctionne. (Certes, la désérialisation des champs final à l'aide d'une méthode readObject personnalisée présente des problèmes ... bien que vous puissiez les contourner à l'aide de hacks de réflexion.)

Il n'y a pas d'alternative à la finale,

Correct.

mais il y a wrapper + privé

Oui, modulo que (à proprement parler) un getter non synchronisé pour un champ non final peut être non thread-safe ... même s'il est initialisé lors de la construction de l'objet et ensuite jamais changé!

et énumérations.

Résout un problème différent. Et enums peut être modifiable.

74
Stephen C

Le mot-clé final est généralement utilisé pour préserver l'immuabilité. Utiliser final pour les classes ou les méthodes revient à empêcher la rupture des liens entre les méthodes. Par exemple, supposons que l'implémentation d'une méthode de classe X suppose que la méthode M se comportera d'une certaine manière. Déclarer X ou M comme final empêchera les classes dérivées de redéfinir M de manière à provoquer un comportement incorrect de X.

2
Katie