Comment fonctionne le mot clé «final» dans Java? (Je peux toujours modifier un object.)

En Java, nous utilisons le mot clé final avec des variables pour spécifier que ses valeurs ne doivent pas être modifiées. Mais je vois que vous pouvez changer la valeur dans le constructeur / les méthodes de la classe. Encore une fois, si la variable est static c’est une erreur de compilation.

Voici le code:

 import java.util.ArrayList; import java.util.List; class Test { private final List foo; public Test() { foo = new ArrayList(); foo.add("foo"); // Modification-1 } public static void main(Ssortingng[] args) { Test t = new Test(); t.foo.add("bar"); // Modification-2 System.out.println("print - " + t.foo); } } 

Le code ci-dessus fonctionne bien et aucune erreur.

Maintenant, changez la variable en tant que static :

 private static final List foo; 

Maintenant, c’est une erreur de compilation. Comment fonctionne cette final ?

Vous êtes toujours autorisé à initialiser une variable final . Le compilateur s’assure que vous ne pouvez le faire qu’une seule fois.

Notez que l’appel de méthodes sur un object stocké dans une variable final n’a rien à voir avec la sémantique de final . En d’autres termes: final ne concerne que la référence elle-même et non le contenu de l’object référencé.

Java n’a pas de concept d’immuabilité d’object; Ceci est réalisé en concevant l’object avec soin, et est une entreprise loin d’être sortingviale.

Ceci est une question d’entrevue préférée . Avec ces questions, l’intervieweur essaie de déterminer si vous comprenez bien le comportement des objects en ce qui concerne les constructeurs, les méthodes, les variables de classe (variables statiques) et les variables d’instance.

 import java.util.ArrayList; import java.util.List; class Test { private final List foo; public Test() { foo = new ArrayList(); foo.add("foo"); // Modification-1 } public void setFoo(List foo) { //this.foo = foo; Results in comstack time error. } } 

Dans le cas ci-dessus, nous avons défini un constructeur pour ‘Test’ et lui avons donné la méthode ‘setFoo’.

À propos du constructeur: Le constructeur peut être appelé une seule fois par création d’object en utilisant le new mot-clé. Vous ne pouvez pas appeler le constructeur plusieurs fois, car le constructeur n’est pas conçu pour le faire.

A propos de la méthode: Une méthode peut être invoquée autant de fois que vous le souhaitez (même jamais) et le compilateur le sait.

Scénario 1

 private final List foo; // 1 

foo est une variable d’ instance . Lorsque nous créons un object de classe Test la variable d’instance foo sera copiée dans l’object de la classe Test . Si nous assignons foo à l’intérieur du constructeur, le compilateur sait que le constructeur ne sera appelé qu’une fois, il n’y a donc aucun problème à l’assigner dans le constructeur.

Si nous assignons foo intérieur d’une méthode, le compilateur sait qu’une méthode peut être appelée plusieurs fois, ce qui signifie que la valeur devra être modifiée plusieurs fois, ce qui n’est pas autorisé pour une variable final . Donc, le compilateur décide que le constructeur est un bon choix! Vous pouvez affecter une valeur à une dernière variable une seule fois.

Scénario 2

 private static final List foo = new ArrayList(); 

foo est maintenant une variable statique . Lorsque nous créons une instance de classe Test , foo ne sera pas copié sur l’object car foo est statique. Maintenant, foo n’est pas une propriété indépendante de chaque object. Ceci est une propriété de la classe Test . Mais foo peut être vu par plusieurs objects et si chaque object créé en utilisant le new mot-clé invoquera finalement le constructeur Test qui modifie la valeur lors de la création de plusieurs objects (N’oubliez pas que static foo n’est pas copié dans chaque object, mais est partagé entre plusieurs objects.)

Scénario 3

 t.foo.add("bar"); // Modification-2 

Ci Modification-2 dessus Modification-2 est de votre question. Dans le cas ci-dessus, vous ne modifiez pas le premier object référencé, mais vous ajoutez du contenu dans foo qui est autorisé. Le compilateur se plaint si vous essayez d’atsortingbuer un new ArrayList() à la variable de référence foo .
Règle Si vous avez initialisé une variable final , vous ne pouvez pas la modifier pour faire référence à un object différent. (Dans ce cas ArrayList )

les classes finales ne peuvent pas être sous-classées
les méthodes finales ne peuvent pas être remplacées. (Cette méthode est en super classe)
les méthodes finales peuvent remplacer. (Lisez ceci de manière grammaticale. Cette méthode est dans une sous-classe)

Le mot clé final peut être utilisé de plusieurs manières:

  • Une classe finale ne peut pas être sous-classée.
  • Une méthode finale ne peut pas être remplacée par des sous-classes
  • Une variable finale ne peut être initialisée qu’une fois

Autre usage:

  • Lorsqu’une classe interne anonyme est définie dans le corps d’une méthode, toutes les variables déclarées finales dans la scope de cette méthode sont accessibles depuis la classe interne.

Une variable de classe statique existera dès le début de la JVM et devrait être initialisée dans la classe. Le message d’erreur n’apparaîtra pas si vous faites cela.

Le mot-clé final peut être interprété de deux manières différentes en fonction de son utilisation:

Types de valeur: pour int s, double s, etc., la valeur ne peut pas changer,

Types de référence: pour les références aux objects, final garantit que la référence ne changera jamais, ce qui signifie qu’elle se référera toujours au même object. Il ne garantit en rien que les valeurs à l’intérieur de l’object renvoyé restnt les mêmes.

En tant que tel, final List foo; s’assure que foo fait toujours référence à la même liste, mais le contenu de cette liste peut changer avec le temps.

Si vous définissez foo static, vous devez l’initialiser dans le constructeur de la classe (ou dans la ligne où vous le définissez) comme dans les exemples suivants.

Constructeur de classe (pas d’instance):

 private static final List foo; static { foo = new ArrayList(); } 

En ligne:

 private static final List foo = new ArrayList(); 

Le problème ici n’est pas le fonctionnement du modificateur final , mais plutôt le fonctionnement du modificateur static .

Le modificateur final impose une initialisation de votre référence au moment où l’appel à votre constructeur est terminé (vous devez l’initialiser dans le constructeur).

Lorsque vous initialisez un atsortingbut en ligne, il est initialisé avant l’exécution du code que vous avez défini pour le constructeur. Vous obtenez donc les résultats suivants:

  • si foo est static , foo = new ArrayList() sera exécuté avant que le constructeur static{} vous avez défini pour votre classe ne soit exécuté
  • si foo n’est pas static , foo = new ArrayList() sera exécuté avant l’exécution de votre constructeur

Lorsque vous n’initialisez pas un atsortingbut en ligne, le final modificateur impose que vous l’initialisiez et que vous devez le faire dans le constructeur. Si vous avez également un modificateur static , le constructeur dans lequel vous devrez initialiser l’atsortingbut est le bloc d’initialisation de la classe: static{} .

L’erreur que vous obtenez dans votre code provient du fait que static{} est exécuté lorsque la classe est chargée, avant le moment où vous instanciez un object de cette classe. Ainsi, vous n’aurez pas initialisé foo lors de la création de la classe.

Pensez au bloc static{} tant que constructeur d’un object de type Class . C’est ici que vous devez effectuer l’initialisation de vos atsortingbuts de classe static final (si cela n’est pas fait en ligne).

Note latérale:

Le modificateur final assure la constance uniquement pour les types et les références primitifs.

Lorsque vous déclarez un object final , ce que vous obtenez est une référence final à cet object, mais l’object lui-même n’est pas constant.

Ce que vous réalisez réellement en déclarant un atsortingbut final est que, une fois que vous déclarez un object pour votre objective spécifique (comme la final List que vous avez déclarée), seul cet object sera utilisé à cette fin: vous ne pourrez pas pour changer la List foo en une autre List , mais vous pouvez toujours modifier votre List en ajoutant / supprimant des éléments (la List vous utilisez sera la même, uniquement avec son contenu modifié).

C’est une très bonne question d’entretien. Parfois, ils peuvent même vous demander quelle est la différence entre un object final et un object immuable.

1) Lorsque quelqu’un mentionne un object final, cela signifie que la référence ne peut pas être modifiée, mais que son état (variables d’instance) peut être modifié.

2) Un object immuable est un object dont l’état ne peut pas être modifié, mais sa référence peut être modifiée. Ex:

  Ssortingng x = new Ssortingng("abc"); x = "BCG"; 

La variable ref peut être modifiée pour pointer une chaîne différente, mais la valeur de “abc” ne peut pas être modifiée.

3) Les variables d’instance (champs non statiques) sont initialisées lors de l’appel d’un constructeur. Vous pouvez donc initialiser les valeurs de vos variables à l’intérieur d’un constructeur.

4) “Mais je vois que vous pouvez changer la valeur dans le constructeur / les méthodes de la classe”. – Vous ne pouvez pas le changer dans une méthode.

5) Une variable statique est initialisée pendant le chargement de la classe. Donc, vous ne pouvez pas initialiser à l’intérieur d’un constructeur, cela doit être fait avant lui. Vous devez donc affecter des valeurs à une variable statique pendant la déclaration elle-même.

Il convient de mentionner quelques définitions simples:

Classes / Méthodes

Vous pouvez déclarer certaines ou toutes les méthodes d’une classe comme final , afin d’indiquer que la méthode ne peut pas être remplacée par des sous-classes.

Les variables

Une fois qu’une variable final a été initialisée, elle contient toujours la même valeur.

final évite fondamentalement l’écrasement / la sur-inscription par quelque chose (sous-classes, variable “réaffecter”), selon les cas.

Supposons que vous ayez deux tirelires, rouge et blanc. Vous n’atsortingbuez à ces tirelires que deux enfants et ils ne sont pas autorisés à échanger leurs boîtes. Vous avez donc des tirelires rouges ou blanches (final) que vous ne pouvez pas modifier mais vous pouvez mettre de l’argent dans votre boîte. Personne ne s’en soucie (Modification-2).

final est un mot-clé réservé à Java pour restreindre l’utilisateur et il peut être appliqué aux variables membres, aux méthodes, aux classes et aux variables locales. Les variables finales sont souvent déclarées avec le mot-clé static en Java et sont traitées comme des constantes. Par exemple:

 public static final Ssortingng hello = "Hello"; 

Lorsque nous utilisons le mot-clé final avec une déclaration de variable, la valeur stockée dans cette variable ne peut plus être modifiée.

Par exemple:

 public class ClassDemo { private final int var1 = 3; public ClassDemo() { ... } } 

Remarque : Une classe déclarée comme finale ne peut pas être étendue ou héritée (c’est-à-dire qu’il ne peut pas y avoir de sous-classe de la super classe). Il est également bon de noter que les méthodes déclarées comme finales ne peuvent pas être remplacées par des sous-classes.

Les avantages de l’utilisation du mot clé final sont abordés dans ce fil de discussion .

Le final mot-clé de Java est utilisé pour restreindre l’utilisateur. Le mot-clé java final peut être utilisé dans de nombreux contextes. La finale peut être:

  1. variable
  2. méthode
  3. classe

Le mot clé final peut être appliqué avec les variables, une variable final sans valeur, appelée variable final vierge ou variable final non initialisée. Il ne peut être initialisé que dans le constructeur. La variable final vierge peut être static et sera initialisée uniquement dans le bloc static .

Variable Java finale:

Si vous définissez une variable comme final , vous ne pouvez pas modifier la valeur de la variable final (elle sera constante).

Exemple de variable final

Il y a une variable finale speedlimit, nous allons changer la valeur de cette variable, mais elle ne peut pas être modifiée car la variable finale une fois affectée ne peut jamais être modifiée.

 class Bike9{ final int speedlimit=90;//final variable void run(){ speedlimit=400; // this will make error } public static void main(Ssortingng args[]){ Bike9 obj=new Bike9(); obj.run(); } }//end of class 

Classe finale Java:

Si vous définissez une classe comme final , vous ne pouvez pas la prolonger .

Exemple de classe finale

 final class Bike{} class Honda1 extends Bike{ //cannot inherit from final Bike,this will make error void run(){ System.out.println("running safely with 100kmph"); } public static void main(Ssortingng args[]){ Honda1 honda= new Honda(); honda.run(); } } 

Méthode Java finale:

Si vous définissez une méthode comme finale, vous ne pouvez pas la remplacer .

Exemple de méthode final (run () dans Honda ne peut pas remplacer run () dans Bike)

 class Bike{ final void run(){System.out.println("running");} } class Honda extends Bike{ void run(){System.out.println("running safely with 100kmph");} public static void main(Ssortingng args[]){ Honda honda= new Honda(); honda.run(); } } 

partagé depuis: http://www.javatpoint.com/final-keyword

Lorsque vous le rendez statique final, il doit être initialisé dans un bloc d’initialisation statique

  private static final List foo; static { foo = new ArrayList(); } public Test() { // foo = new ArrayList(); foo.add("foo"); // Modification-1 } 

Le mot-clé final indique qu’une variable ne peut être initialisée qu’une fois. Dans votre code, vous effectuez une seule initialisation de la version finale afin que les termes soient satisfaits. Cette instruction effectue l’initialisation solitaire de foo . Notez que final ! = Immutable, cela signifie seulement que la référence ne peut pas changer.

 foo = new ArrayList(); 

Lorsque vous déclarez foo comme static final la variable doit être initialisée lorsque la classe est chargée et ne peut pas compter sur l’instanciation (appel appelé constructeur) pour initialiser foo car les champs statiques doivent être disponibles sans instance d’une classe. Il n’y a aucune garantie que le constructeur aura été appelé avant d’utiliser le champ statique.

Lorsque vous exécutez votre méthode dans le scénario static final , la classe Test est chargée avant l’instanciation de t pour l’instant, il n’y a pas d’instanciation de foo ce qui signifie qu’elle n’a pas été initialisée. À ce stade, je suppose que votre code NullPointerException une NullPointerException lorsque vous essayez d’append un élément à la liste.

  1. La variable finale étant non statique, elle peut être initialisée dans constructeur. Mais si vous le rendez statique, il ne peut pas être initialisé par constructeur (car les constructeurs ne sont pas statiques).
  2. L’ajout à la liste ne devrait pas s’arrêter en rendant la liste définitive. final lie uniquement la référence à un object particulier. Vous êtes libre de changer l’état de cet object, mais pas l’object lui-même.

Lisez toutes les réponses.

Il existe un autre cas d’utilisateur où le mot clé final peut être utilisé, c’est-à-dire dans un argument de méthode:

 public void showCaseFinalArgumentVariable(final int someFinalInt){ someFinalInt = 9; // won't comstack as the argument is final } 

Peut être utilisé pour une variable qui ne doit pas être modifiée.

J’ai pensé à écrire une réponse actualisée et approfondie ici.

final mot clé final peut être utilisé à plusieurs endroits.

  1. Des classes

Une final class signifie qu’aucune autre classe ne peut prolonger cette classe finale. Lorsque Java Run Time ( JRE ) sait qu’une référence d’object est dans le type d’une classe finale (disons F), il sait que la valeur de cette référence ne peut être que dans le type F.

Ex:

 F myF; myF = new F(); //ok myF = someOther; //someOther cannot be in type of a child class of F. //because F cannot be extended. 

Ainsi, lorsqu’il exécute une méthode quelconque de cet object, il n’est pas nécessaire de résoudre cette méthode lors de l’exécution à l’aide d’une table virtuelle . c’est-à-dire que le polymorphism d’exécution ne peut pas être appliqué. Donc, le temps d’exécution ne s’en préoccupe pas. Cela signifie qu’il permet de gagner du temps de traitement, ce qui améliorera les performances.

  1. méthodes

Une final method de n’importe quelle classe signifie que toute classe enfant qui étend cette classe ne peut pas remplacer cette ou ces méthodes finales. Le comportement d’exécution dans ce scénario est donc tout à fait identique au comportement précédent que j’ai mentionné pour les classes.

  1. champs, variables locales, parameters de méthode

Si l’on spécifiait un type quelconque ci-dessus comme final , cela signifie que la valeur est déjà finalisée, donc la valeur ne peut pas être modifiée .

Ex:

Pour les champs, parameters locaux

 final FinalClass fc = someFC; //need to assign straight away. otherwise comstack error. final FinalClass fc; //comstack error, need assignment (initialization inside a constructor Ok, constructor can be called only once) final FinalClass fc = new FinalClass(); //ok fc = someOtherFC; //comstack error fc.someMethod(); //no problem someOtherFC.someMethod(); //no problem 

Pour les parameters de méthode

 void someMethod(final Ssortingng s){ s = someOtherSsortingng; //comstack error } 

Cela signifie simplement que la valeur de la valeur de référence final ne peut pas être modifiée. c’est-à-dire qu’une seule initialisation est autorisée. Dans ce scénario, au moment de l’exécution, JRE sachant que les valeurs ne peuvent pas être modifiées, il charge toutes ces valeurs finalisées (des références finales) dans le cache L1 . Parce qu’il n’a pas besoin de se recharger encore et encore depuis la mémoire principale . Sinon, il charge le cache L2 et charge de temps en temps la mémoire principale. Donc, c’est aussi une amélioration de la performance.

Donc, dans les trois scénarios ci-dessus, lorsque nous n’avons pas spécifié le mot clé final dans les endroits que nous pouvons utiliser, nous n’avons pas à nous inquiéter, les optimisations du compilateur le feront pour nous. Il y a aussi beaucoup d’autres choses que les optimisations du compilateur font pour nous. 🙂

Avant tout sont corrects. De plus, si vous ne voulez pas que les autres créent des sous-classes de votre classe, déclarez votre classe comme finale. Ensuite, il devient le niveau feuille de votre hiérarchie d’arborescence de classes que personne ne peut l’étendre davantage. C’est une bonne pratique d’éviter la grande hiérarchie des classes.

Tout d’abord, l’emplacement dans votre code où vous initialisez (c.-à-d. Atsortingbuer pour la première fois) foo est ici:

 foo = new ArrayList(); 

foo est un object (avec le type List) donc c’est un type de référence , pas un type de valeur (comme int). En tant que tel, il contient une référence à un emplacement de mémoire (par exemple 0xA7D2A834) où vos éléments de liste sont stockés. Des lignes comme ça

 foo.add("foo"); // Modification-1 

ne changez pas la valeur de foo (qui, encore une fois, n’est qu’une référence à un emplacement de mémoire). Au lieu de cela, ils ajoutent simplement des éléments dans cet emplacement mémoire référencé. Pour violer le mot-clé final , vous devrez réessayer comme suit:

 foo = new ArrayList(); 

Cela vous donnerait une erreur de compilation.


Maintenant, avec cela, réfléchissez à ce qui se passe lorsque vous ajoutez le mot-clé statique .

Lorsque vous n’avez pas le mot-clé static, chaque object qui instancie la classe a sa propre copie de foo. Par conséquent, le constructeur assigne une valeur à une nouvelle copie vierge de la variable foo, ce qui est parfaitement correct.

Cependant, lorsque vous avez le mot-clé static, un seul foo existe dans la mémoire associée à la classe. Si vous deviez créer deux objects ou plus, le constructeur tenterait à chaque fois de les réaffecter, en violation du mot clé final .

Voici différents contextes où la finale est utilisée.

Variables finales Une variable finale ne peut être atsortingbuée qu’une seule fois. Si la variable est une référence, cela signifie que la variable ne peut pas être re-liée à un autre object.

 class Main { public static void main(Ssortingng args[]){ final int i = 20; i = 30; //Comstackr Error:cannot assign a value to final variable i twice } } 

la variable finale peut être assignée plus tard (pas obligatoire d’atsortingbuer une valeur lorsqu’elle est déclarée), mais une seule fois.

Les classes finales Une classe finale ne peut pas être étendue (héritée)

 final class Base { } class Derived extends Base { } //Comstackr Error:cannot inherit from final Base public class Main { public static void main(Ssortingng args[]) { } } 

Méthodes finales Une méthode finale ne peut pas être remplacée par des sous-classes.

 //Error in following program as we are trying to override a final method. class Base { public final void show() { System.out.println("Base::show() called"); } } class Derived extends Base { public void show() { //Comstackr Error: show() in Derived cannot override System.out.println("Derived::show() called"); } } public class Main { public static void main(Ssortingng[] args) { Base b = new Derived();; b.show(); } }