Classe finale
Lorsqu'une classe est définie comme une classe finale, cela signifie que la classe ne peut pas être héritée par d'autres classes, c'est-à-dire qu'elle ne peut pas être utilisée après les étendues. Sinon, vous obtiendrez une erreur pendant la compilation.
package com.iderzheng.FinalKeyword; Public Final Class FinalClass {} // Erreur: Impossible de hériter de FinalClass PackageClass étend FinalClass {} Java prend en charge la définition de la classe comme final, ce qui semble violer les principes de base de la programmation orientée objet. Cependant, en revanche, la classe fermée garantit également que toutes les méthodes de la classe sont fixes et qu'il n'y aura pas de remplacements de sous-classe à charger dynamiquement. Cela offre plus de possibilités pour le compilateur d'optimiser. Le meilleur exemple est String, qui est la classe finale. Le compilateur Java peut transformer directement les constantes de chaîne (celles contenues dans des devis doubles) en objets de chaîne, et en même temps optimiser directement l'opération + l'opération en nouvelles constantes, car la modification finale garantit qu'aucune sous-classe ne renvoie différentes valeurs pour les opérations d'épissage.
Pour toutes les définitions de classes différentes - les classes de niveau supérieur (globales ou visibles sur package), les classes imbriquées (classes imbriquées internes ou statiques) peuvent être modifiées avec Final. Cependant, en général, Final est principalement utilisé pour modifier les classes définies comme publiques, car pour les classes non globales, les modificateurs d'accès ont limité leur visibilité, et il est déjà difficile de hériter de ces classes, il n'est donc pas nécessaire d'ajouter une couche de restrictions finales.
Il est également mentionné que bien que les classes anonymes ne puissent pas être héritées, elles ne sont pas limitées à la finale par le compilateur.
import java.lang.reflect.modificier; classe publique main {public static void main (string [] args) {runnable anonymous = new runnable () {@Override public void run () {}}; System.out.println (modificateur.isfinal (anonymous.getClass (). GetModificaires ())); }}Sortir:
FAUX
Méthode finale
Le polymorphisme est étroitement lié au concept d'hérédité, ce qui implique la différence entre les concepts de remplacement et de cachette (pour plus de commodité, les éléments suivants sont collectivement appelés "réécriture"). Cependant, contrairement à la définition de la méthode en C ++, que le mot-clé virtuel soit ajouté à la sous-classe, que la méthode de signature de sous-classe soit écrasée ou masquée, en Java, les sous-classes utilisent la même signature de méthode pour écraser la méthode de la classe parent, qui formera une méthode de classe cachée (méthode statique), tandis que les méthodes d'objets (méthode non statique) uniquement. Étant donné que Java permet l'accès direct aux méthodes de classe via des objets, Java ne permet pas aux méthodes de classe et aux méthodes d'objets d'avoir la même signature dans la même classe.
La classe finale définit que toute la classe ne peut pas être héritée, et cela signifie également que toutes les méthodes de la classe ne peuvent pas être couvertes et cachées par les sous-classes. Lorsque la classe n'est pas modifiée par Final, certaines méthodes peuvent toujours être modifiées en utilisant Final pour empêcher que ces méthodes soient réécrites par des sous-classes.
De même, une telle conception détruit le polymorphisme orienté objet, mais la méthode finale peut assurer le déterminisme de son exécution, assurant ainsi la stabilité des appels de méthode. Dans certaines conceptions de cadre, certaines méthodes implémentées de classes abstraites sont souvent considérées comme limitées à Final, car certains code de pilote dans le cadre s'appuieront sur ces méthodes pour atteindre les objectifs établis, il n'y a donc pas de sous-classes qui le couvrent.
L'exemple suivant montre le rôle de la modification finale dans différents types de méthodes:
package com.iderzheng.other; classe publique FinalMethods {public static void publicStatingMethod () {} public final void publicfinalMethod () {} public static final void publicStaticFinalMethod () {} Protected Final Void ProtectedFinalMethod () {} Final Final Final Final FinalMethod final. void staticFinalMethod () {} private statique final void privatestaticfinalMethod () {} private fin Void privateFinalMethod () {}} package com.iderzheng.finalkeyword; import com.iderzheng.other.FinalMethods; Méthodes de classe publique étend FinalMethods {public static void publicStaticMethod () {} // error: Impossible de remplacer public final void publicFinalMethod () {} // Erreur: Impossible de remplacer le public final statique Void publicStaticFinalMethod () {} // error: Impossible de remplacer le public final final STATIC PublicSaticFinalMethod () {} void publicStaticFinalMethod () {} // Erreur: Impossible de remplacer le void final protégé protégé Protégé FinalMethod () {} // Erreur: Impossible de remplacer le void statique protégée VOID ProtéctedStatic Final {} private final void privatefinalMethod () {}} Tout d'abord, notez que dans l'exemple ci-dessus, les méthodes finales et les méthodes sont définies dans différents packages. Pour le premier PublicStaticMethod, la sous-classe réécrit avec succès la méthode statique de la classe parent, mais parce que c'est une méthode statique, ce qui se passe est en fait "caché". Plus précisément, les méthodes d'appel.PublicStaticMethod () exécuteront l'implémentation dans la classe de méthodes. Lors de l'appel finalMethods.PublicStaticMethod (), l'implémentation ne se produira pas avec la charge polymorphe de la sous-classe, mais utilisera directement l'implémentation de FinalMethods. Par conséquent, lors de l'utilisation de sous-classes pour accéder aux méthodes, la visibilité des méthodes signées par la classe parent est cachée.
Pour la méthode globale PublicFinalMethod, comme décrit dans la méthode de modification finale, la sous-classe est interdite de l'écraser, et une exception sera lancée au moment de la compilation. Cependant, le nom de la méthode est le même dans la sous-classe, mais il a un paramètre, tel que: publicFinalMethod (String x) est OK, car il s'agit de la signature de la méthode synchrone.
Dans Intellij, l'IDE montre un avertissement à la méthode publique de FinalMethod: «statique» déclarée «finale». Il semble redondant, mais à partir de l'exemple, on peut voir que final interdit également les définitions de sous-classe à partir de méthodes statiques pour le cacher. Dans le développement réel, le comportement de la définition des mêmes méthodes statiques de sous-classes et de classes de parents est extrêmement souhaitable, car les méthodes cachées obligent les développeurs à prêter attention à l'utilisation de différents noms de classe pour définir différents effets, ce qui est facile à provoquer des erreurs. De plus, au sein de la classe, vous pouvez appeler directement des méthodes statiques sans utiliser le nom de classe. Lorsque le développeur hérite à nouveau, il peut ne pas remarquer l'existence cachée. Par défaut, lors de l'utilisation de la méthode de la classe parent, il constatera que ce n'est pas le résultat attendu. Par conséquent, les méthodes statiques doivent être définitivement par défaut et ne doivent pas être cachées, donc IDE pense que c'est une modification inutile.
Les méthodes de modification protégée et de modification publique dans la classe parent sont visibles pour la sous-classe, de sorte que la situation de modification finale des méthodes protégées est la même que celle des méthodes publiques. Il faut mentionner que dans le développement réel, les méthodes statiques protégées sont généralement rarement définies car ces méthodes sont trop pratiques.
Pour la méthode du package de classe parent, les sous-classes sous différents packages sont invisibles. La méthode privée a été personnalisée et seule la classe parent peut y accéder. Le compilateur permet donc aux sous-classes de définir la même méthode. Mais cela ne forme pas de remplacement ou de masque, car la classe parent a caché ces méthodes via des modificateurs, non causés par la réécriture des sous-classes. Bien sûr, si la sous-classe et la classe parent sont dans le même package, la situation sera la même que le public précédent et protégé.
Pourquoi la méthode finale est-elle efficace?
La méthode finale utilisera le mécanisme intégré pour optimiser le ralentissement pendant la compilation. L'optimisation en ligne fait référence à: l'appel du remplacement du code de fonction directement lors de la compilation, plutôt que d'appeler les fonctions à l'exécution. En ligne doit savoir quelle fonction utiliser à la fin lors de la compilation. De toute évidence, il n'est pas possible de l'utiliser sans final. Les méthodes non finales peuvent être réécrites dans les sous-classes. En raison du polymorphisme possible, le compilateur ne peut pas déterminer le type vrai de l'objet pour appeler la méthode à l'avenir pendant l'étape de compilation, et il ne peut pas déterminer la méthode à appeler.
Variable finale
En termes simples, la variable finale de Java ne peut être que l'initialisation une fois, puis la variable est liée à la valeur. Cependant, cette affectation n'a pas nécessairement besoin d'être initialisée immédiatement lorsque la variable est définie. Java prend également en charge différents résultats pour les variables finales à travers des instructions conditionnelles, mais la variable ne peut être attribuée qu'une seule fois dans tous les cas.
Cependant, la variable finale de Java n'est pas une constante absolue, car les variables d'objet de Java ne sont que des valeurs de référence, donc final signifie simplement que la référence ne peut pas être modifiée et que le contenu de l'objet peut toujours être modifié. Par rapport aux pointeurs C / C ++, il ressemble plus à la variable de type * Const que la variable const * de type.
Les variables Java peuvent être divisées en deux catégories: les variables locales (variable locale) et les variables des membres de la classe (champ de classe). Ce qui suit est un code pour introduire leur situation d'initialisation séparément.
Variable locale
Les variables locales se réfèrent principalement aux variables définies dans les méthodes. Ils disparaîtront et deviendront inaccessibles après la méthode. Il existe un cas particulier qui peut être divisé en: Paramètres de fonction. Pour ce cas, son initialisation est liée aux paramètres transmis lorsque la fonction est appelée.
Pour d'autres variables locales, ils sont définis dans la méthode et leurs valeurs peuvent être initialisées conditionnellement:
Méthode de chaîne publique (final booléen finalParam) {// Erreur: le paramètre final finalParam ne peut pas être attribué // finalParam = true; objet final finallocal = finalParam? Nouveau objet (): null; final int finalvar; if (finallocal! = null) {finalvar = 21; } else {finalvar = 7; } // Erreur: la variable finalVar a peut-être déjà été affectée // finalVar = 80; Final FinalTret final; switch (finalvar) {cas 21: finalret = "me"; casser; Cas 7: finalret = "She"; casser; par défaut: finalRet = null; } return finalret;} À partir de l'exemple ci-dessus, on peut voir que les paramètres de fonction modifiés par final ne peuvent pas se voir attribuer une nouvelle valeur, mais d'autres variables locales finales peuvent se voir attribuer une valeur dans une instruction conditionnelle. Cela offre également une certaine flexibilité pour final.
Bien sûr, toutes les conditions de l'instruction conditionnelle doivent contenir des affectations aux variables locales finales, sinon vous obtiendrez une erreur que la variable ne peut pas être initialisée.
Méthode de chaîne publique (objet final finalParam) {final int finalvar; if (finalParam! = null) {finalvar = 21; } Final String finalRet; // Erreur: la variable finalvar n'a peut-être pas été initialisée Switch (finalVar) {case 21: finalret = "me"; casser; Cas 7: finalret = "She"; casser; } // Erreur: la variable finalret n'aurait peut-être pas été initialisée Retour Finalret;} En théorie, les variables locales ne sont pas nécessaires pour être définies comme finales, et une méthode de conception raisonnable devrait être en mesure de bien maintenir les variables locales. C'est juste que lorsque vous utilisez des fonctions anonymes pour effectuer des fermetures dans les méthodes Java, Java exige que la variable locale référencée soit définie comme finale:
Méthode publique (String String) {int Integer = 12; return new Runnable () {@Override public void run () {// error: doit être déclaré final System.out.println (string); // Erreur: doit être déclaré System Final.out.println (entier); }};}Champ de classe
Les variables des membres de la classe peuvent en fait être divisées en deux types: statique et non statique. Pour les variables de membres de la classe statique, car ils sont liés aux classes, en plus d'être directement initialisés au moment de la définition, ils peuvent également être placés dans un bloc statique, et l'utilisation de ce dernier peut exécuter des instructions plus complexes:
package com.iderzheng.FinalKeyword; import java.util.hashset; import java.util.linkedhashset; import java.util.set; classe publique StaticFinalFields {statique final int static_final_init_inline = 7; Set final statique <Integer> static_final_init_static_block; / ** BLOC STATIQUE ** / STATIQUE {if (System.Currenttimemillis ()% 2 == 0) {static_final_init_static_block = new hashset <> (); } else {static_final_init_static_block = new LinkedHashSet <> (); } Static_final_init_static_block.add (7); Static_final_init_static_block.add (21); }}Il existe également des blocs non statiques en Java qui peuvent initialiser les variables des membres non statiques, mais pour ces variables, elles sont souvent placées dans le constructeur pour l'initialisation. Bien sûr, il est nécessaire de s'assurer que chaque variable finale est initialisée une fois dans le constructeur. Si d'autres constructeurs sont appelés via ce (), ces variables finales ne peuvent plus être attribuées dans le constructeur.
package com.iderzheng.FinalKeyword; classe publique finalFields {final long final_init_inline = System.currenttimemillis (); final long final_init_block; final long final_init_constructor; / ** Bloc initial ** / {final_init_block = System.NanoTime (); } FinalFields () {this (217); } FinalFields (boolean bool) {final_init_constructor = 721; } FinalFields (long init) {final_init_constructor = init; }}Lorsque Final est utilisé pour modifier les classes (classe) et les méthodes (méthode), il affecte principalement l'héritage orienté objet. Sans héritage, il n'y aura aucune dépendance au code de la sous-classe sur la classe parent. Par conséquent, lors de la modification du code pendant la maintenance, il n'a pas besoin de déterminer si l'implémentation de la sous-classe sera détruite, ce qui le rend plus pratique. Lorsqu'il est utilisé sur une variable, Java garantit que la valeur variable ne sera pas modifiée. Si une autre conception garantit que les membres de la classe ne peuvent pas être modifiés, alors la variable entière peut être transformée en constante, ce qui est très bénéfique pour la programmation multi-thread. Par conséquent, Final a un très bon effet sur la maintenance du code.