Comment fonctionne le mot-clé" final " en Java? (Je peux encore modifier un objet.)


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 modifier la valeur dans le constructeur / méthodes de la classe. Encore une fois, si la variable est static alors 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(String[] 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 static:

private static final List foo;

Maintenant, c'est une erreur de compilation. Comment est-ce final vraiment travailler?

Author: user2864740, 2013-03-27

18 answers

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

Notez que l'appel de méthodes sur un objet 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'objet référencé.

Java n'a pas de concept d'immutabilité d'objet; ceci est réalisé en concevant soigneusement l'objet, et est loin d'être trivial efforcer.

 440
Author: Marko Topolnik, 2014-03-23 23:45:06

Ceci est une question d'entrevue préférée. Avec ces questions, l'intervieweur essaie de savoir dans quelle mesure vous comprenez le comportement des objets 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 compile time error.
    }
}

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

À propos du constructeur: Le constructeur ne peut être invoqué que une fois par objet création en utilisant le mot-clé new. Vous ne pouvez pas invoquer le constructeur plusieurs fois, car le constructeur n'est pas conçu pour le faire.

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

Scénario 1

private final List foo;  // 1

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

Si nous cédons foo à l'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 reprises, ce qui n'est pas autorisé pour un final variable. Le compilateur décide donc que le constructeur est un bon choix! Vous pouvez attribuer une valeur à une variable finale uniquement une fois.

Scénario 2

private static final List foo = new ArrayList();

foo est maintenant une variable statique . Lorsque nous créons une instance de Test class, foo ne sera pas copié dans l'objet car foo est statique. Maintenant, foo n'est pas une propriété indépendante de chaque objet. Ceci est une propriété de la classe Test. Mais foo peut être vu par plusieurs objets et si chaque objet créé en utilisant le mot clé new qui invoquera finalement le constructeur Test qui modifie la valeur à l'heure de création de plusieurs objets (Remember static foo n'est pas copiée dans chaque objet, mais est partagée entre plusieurs objets.)

Scénario 3

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

Ci-dessus Modification-2 est de votre question. Dans le cas ci-dessus, vous ne modifiez pas le premier objet référencé, mais vous ajoutez du contenu dans foo qui est autorisé. Le compilateur se plaint si vous essayez d'attribuer un new ArrayList() à la variable de référence foo.
la Règle Si vous avez initialisé un final variable, alors vous ne pouvez pas changer pour faire référence à un objet 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 superclasse)
les méthodes finales peuvent remplacer. (Lisez ceci de manière grammaticale. Cette méthode est dans une sous-classe)

 505
Author: AmitG, 2017-07-18 03:27:13

Le mot-clé final a de nombreuses façons d'utiliser:

  • 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 seule fois

Autre utilisation:

  • Lorsqu'une classe interne anonyme est définie dans le corps d'une méthode, toutes les variables déclarées définitives dans la portée de cette méthode sont accessible à partir de l'intérieur classe

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.

 183
Author: czupe, 2014-02-17 01:28:43

Le mot-clé final peut être interprété de deux manières différentes en fonction de ce sur quoi il est utilisé:

Types de Valeur: Pour ints, doubles etc, il veillera à ce que la valeur ne peut pas changer,

Types de référence: Pour les références à des objets, finalgarantit que la référence ne changera jamais, ce qui signifie qu'elle fera toujours référence au même objet. Il ne donne aucune garantie quant aux valeurs à l'intérieur de l'objet auquel il est fait référence de même.

En tant Que tel, final List<Whatever> foo; garantit que foo renvoie toujours à cette liste, mais le contenu de de ladite liste peut changer au fil du temps.

 46
Author: Smallhacker, 2017-07-31 02:51:15

Si vous rendez foo statique, vous devez l'initialiser dans le constructeur de classe (ou en ligne où vous le définissez) comme 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 comment fonctionne le modificateur final, mais plutôt comment fonctionne le modificateur static.

Le modificateur final applique une initialisation de votre référence au moment où l'appel à votre constructeur se termine (c'est-à-dire que vous devez l'initialiser dans le constructeur).

Lorsque vous initialisez un attribut 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 le static{} constructeur, vous avez défini pour votre classe est exécutée
  • si foo n'est pas static, foo = new ArrayList() sera exécuté avant votre constructeur est exécuté

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

L'erreur que vous obtenez dans votre code est le fait que static{} est exécuté lorsque la classe est chargée, avant le moment d'instancier un objet de cette classe. Ainsi, vous n'aurez pas initialisé foo lors de la création de la classe.

Pensez à le bloc static{} en tant que constructeur pour un objet de type Class. C'est là que vous devez effectuer l'initialisation de vos attributs de classe static final (si ce n'est pas fait en ligne).

Note latérale:

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

Lorsque vous déclarez un final objet, ce que vous obtenez est un final référence de cet objet, mais l'objet lui-même n'est pas constante.

Ce que vous réalisez réellement en déclarant un l'attribut final est que, une fois que vous déclarez un objet pour votre objectif spécifique (comme le final List que vous avez déclaré), cet objet et seulement cet objet seront utilisés à cette fin: vous ne pourrez pas changer List foo en un autre List, mais vous pouvez toujours modifier votre List en ajoutant/supprimant des éléments (le List que vous utilisez sera le même, seulement avec son contenu modifié).

 22
Author: lucian.pantelimon, 2013-03-27 09:24:59

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

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

2) Un objet immuable est celle dont l'état peut - pas être changé, mais sa référence peut être modifié. Ex:

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

Ref la variable x 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 lorsqu'un constructeur est appelé. Vous pouvez donc initialiser des valeurs à vos variables dans un constructeur.

4) "Mais je vois que vous pouvez modifier la valeur dans le constructeur/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. Vous ne pouvez donc pas initialiser à l'intérieur d'un constructeur, cela doit être fait avant même. Si vous devez affecter des valeurs à une variable statique pendant la déclaration elle-même.

 6
Author: user892871, 2014-09-26 07:34:59

Vaut la peine de mentionner quelques définitions simples:

Classes/Méthodes De

Vous pouvez déclarer une partie ou la totalité des 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 fondamentalement, évitez d'écraser / superscribe par quoi que ce soit (sous-classes, variable " réaffecter"), selon le cas.

 6
Author: ivanleoncz, 2018-05-22 13:34:37

Supposons que vous ayez deux tirelires, rouge et blanche. Vous attribuez ces tirelires seulement deux enfants et ils ne sont pas autorisés à échanger leurs boîtes. Vous avez donc des tirelires rouges ou blanches(finale) vous ne pouvez pas modifier la boîte mais vous pouvez mettre de l'argent sur votre boîte.Personne ne s'en soucie (Modification-2).

 4
Author: huseyin, 2016-01-27 20:04:40

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

public static final String hello = "Hello";

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

Par exemple:

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

Note: Une classe déclaré comme final ne peut pas être étendu ou hérité (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 .

 3
Author: Desta Haileselassie Hagos, 2017-05-23 12:10:43

Le final clé en java est utilisé pour restreindre l'utilisateur. Le mot-clé java final peut être utilisé dans de nombreux contextes. Finale peut être:

  1. variable
  2. méthode
  3. classe

Le mot-clé final peut être appliqué avec les variables, une variable final qui n'a pas de valeur, est appelée variable final vide ou variable final non initialisée. Il peut être initialisé dans le constructeur uniquement. La variable vide final peut également être static qui sera initialisée dans le static bloquer uniquement.

Java variable finale:

Si vous faites une variable comme final, vous ne pouvez pas changer la valeur de final variable(Elle sera constante).

Exemple de final variable

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 assignée une valeur 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(String args[]){  
    Bike9 obj=new  Bike9();  
    obj.run();  
    }  
}//end of class  

Classe finale Java:

Si vous faites n'importe quelle classe comme final, vous ne pouvez pas l'étendre.

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(String args[]){  
      Honda1 honda= new Honda();  
      honda.run();  
      }  
  }  

Méthode finale Java:

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

Exemple de méthode final (run() de Honda ne peut pas remplacer run() en Vélo)

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(String args[]){  
   Honda honda= new Honda();  
   honda.run();  
   }  
}  

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

 3
Author: Ali Ziaee, 2018-01-30 07:33:36

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
    }
 1
Author: Evgeniy Dorofeev, 2013-03-27 09:08:24

Le mot-clé final indique qu'une variable ne peut être initialisée qu'une seule fois. Dans votre code, vous n'effectuez qu'une seule initialisation de final afin que les termes soient satisfaits. Cette instruction effectue l'initialisation unique de foo. Notez que final!= immuable, 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 (aka appel au constructeur) pour initialiser foo puisque les champs statiques doivent être disponibles sans instance de 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 à ce moment, il n'y a pas d'instanciation de foo, ce qui signifie qu'elle n'a pas été initialisée, donc foo est défini par défaut pour tous les objets qui sont null. À ce stade, je suppose que votre code lance un NullPointerException lorsque vous tentez d'ajouter un élément à la liste.

 1
Author: Kevin Bowersox, 2013-03-27 09:14:38
  1. Puisque la variable finale est non statique, elle peut être initialisée dans le constructeur. Mais si vous le rendez statique, il ne peut pas être initialisé par le 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 simplement la référence à un objet particulier. Vous êtes libre de changer "l'état" de cet objet, mais pas l'objet lui-même.
 1
Author: Ankit, 2015-06-05 01:50:49

Lisez toutes les réponses.

Il existe un autre cas 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 compile as the argument is final

}

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

 1
Author: Pritam Banerjee, 2017-06-27 23:48:06

J'ai pensé à écrire une réponse mise à jour et approfondie ici.

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

  1. les classes

Un final class signifie que pas de les autres classes peuvent étendre la finale de la classe. Quand Java Temps D'exécution (JRE) sait qu'une référence d'objet est dans le type d'une classe finale (disons F), elle sait que la valeur de cette référence ne peut être que dans le type de 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.

Donc lorsqu'il exécute une méthode de cet objet, que la méthode n'a pas besoin de pour être résolus au moment de l'exécution à l'aide d'un table virtuelle. c'est-à-dire que le polymorphisme d'exécution ne peut pas être appliqué. Donc, le temps d'exécution ne dérange pas à ce sujet. Ce qui signifie qu'il économise du temps de traitement, ce qui améliorera les performances.

  1. méthodes

A final method de toute classe signifie que toute classe enfant étendant cette classe impossible de remplacer qui méthode finale(s). Donc, le comportement d'exécution dans ce scénario est également tout à fait le même avec le comportement précédent que j'ai mentionné pour les classes.

  1. champs, variables locales, paramètres de méthode

Si on a spécifié tout type de ci-dessus comme final, cela signifie que la valeur est déjà finalisée, donc le la valeur ne peut pas être modifiée.

Ex:

Pour les champs, paramètres locaux

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

Pour les paramètres de méthode

void someMethod(final String s){
    s = someOtherString; //compile 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 initialisation est autorisé. Dans ce scénario, en temps d'exécution, puisque JRE sait que les valeurs ne peuvent pas être modifiées, il charge toutes ces valeurs finalisées (des références finales) dans Cache L1. Parce qu'il n'a pas besoin de pour charger de retour encore et encore de la mémoire principale. Sinon il se charge dans le cache L2 et se charge de temps en temps à partir de la mémoire principale. C'est donc aussi une amélioration des performances.

Donc dans l'ensemble au-dessus de 3 scénarios, lorsque nous n'avons pas spécifié de la final clé dans les endroits que nous pouvons utiliser, nous n'avons pas besoin de s'inquiéter, optimisations du compilateur le fera pour nous. Il y a aussi beaucoup d'autres choses que les optimisations du compilateur font pour nous. :)

 1
Author: Supun Wijerathne, 2018-01-14 04:00:49

Surtout sont corrects. De plus, si vous ne voulez pas que les autres créent des sous-classes à partir de votre classe, déclarez votre classe comme finale. Ensuite, il devient le niveau de feuille de votre hiérarchie d'arbre de classe que personne ne peut l'étendre davantage. C'est une bonne pratique pour éviter une énorme hiérarchie de classes.

 0
Author: Shehan Simen, 2013-12-23 02:10:04

Tout d'abord, l'endroit dans votre code où vous initialisez (c'est-à-dire attribuez pour la première fois) foo est ici:

foo = new ArrayList();

Foo est un objet (de type Liste), donc il est un référence type, pas un valeur type (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 celle-ci

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

Ne modifiez pas la valeur de foo (qui, encore une fois, n'est qu'une référence à un emplacement mémoire). Au lieu de cela, ils ont juste ajoutez des éléments dans cet emplacement de mémoire référencé. Pour violer le mot-clé final , vous devez essayer de réaffecter foo comme suit:

foo = new ArrayList();

Que vous donnerait une erreur de compilation.


Maintenant, avec cela à l'écart, pensez à ce qui se passe lorsque vous ajoutez le mot-clé static.

Lorsque vous n'avez PAS le mot-clé static, chaque objet qui instancie la classe a sa propre copie de foo. Par conséquent, le constructeur attribue une valeur à une copie vierge et fraîche de la variable foo, ce qui est parfaitement bien.

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

 0
Author: Niko Bellic, 2014-07-22 19:02:21

Voici différents contextes où final est utilisé.

Variables finales Une variable finale ne peut être attribué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 pour référencer un autre objet.

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

La valeur finale peut être attribuée ultérieurement (non obligatoire pour attribuer une valeur lors de la déclaration), mais une seule fois.

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

final class Base { }
class Derived extends Base { } //Compiler Error:cannot inherit from final Base

public class Main {
   public static void main(String args[]) {
   }
}

Finale méthodes 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() {  //Compiler Error: show() in Derived cannot override
       System.out.println("Derived::show() called");
    }
}     
public class Main {
    public static void main(String[] args) {
        Base b = new Derived();;
        b.show();
    }
}
 0
Author: roottraveller, 2016-10-07 06:26:44