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 changé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);
  }
}

ci-dessus le code fonctionne bien et aucune erreur.

changer maintenant la variable comme static :

private static final List foo;

Maintenant, c'est une erreur de compilation. Comment fonctionne ce final ?

413
demandé sur user2864740 2013-03-27 13:02:18

18 réponses

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

notez que les méthodes d'appel sur un objet stocké dans une variable final n'ont 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'objet immutabilité: ceci est obtenu en concevant soigneusement l'objet, et est une entreprise loin-de-trivial.

440
répondu Marko Topolnik 2014-03-23 23:45:06

C'est un préférés question d'entrevue . Avec ces questions, l'intervieweur essaie de savoir comment vous comprenez le comportement des objets par rapport aux constructeurs, aux méthodes, aux variables de classe (variables statiques) et aux 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 peut être invoqué seulement un temps par création d'objet en utilisant le mot-clé new . Vous ne pouvez pas invoquer le constructeur plusieurs fois, parce que le constructeur n'est pas conçu pour le faire.

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

scénario 1

private final List foo;  // 1

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



Si nous assignons 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 changée plusieurs fois, ce qui n'est pas autorisé pour une variable final . Ainsi, le compilateur décide constructeur est un bon choix! , Vous pouvez affecter une valeur à une variable finale qu'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'objet parce que foo est statique. Maintenant foo n'est pas une propriété indépendante de chaque objet. Il s'agit d'un bien de la catégorie Test . Mais foo peut être vu par plusieurs objets et si chaque objet qui est créé en utilisant le mot-clé new qui va finalement invoquer le constructeur Test qui change la valeur au moment de la création d'objets multiples (rappelez-vous static foo n'est pas copié dans chaque objet, mais est partagé entre plusieurs objets.)

scénario 3

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

au-dessus de Modification-2 est de votre question. Dans le cas ci-dessus, vous ne changez pas le premier référencé objet, 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 .

règle si vous avez initialisé une variable final , alors vous ne pouvez pas la modifier pour faire référence à un objet différent. (Dans ce cas ArrayList )

final les classes ne peuvent pas être subdivisées

final les méthodes ne peuvent être supplantées. (Cette méthode est en superclass)

les méthodes finales peuvent outrepasser. (Lisez ceci de façon grammaticale. Cette méthode est dans une sous-classe)

505
répondu AmitG 2017-07-18 03:27:13

Finale mot a plusieurs façon de l'utiliser:

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

autres utilisations:

  • Lorsqu'un la classe interne anonyme est définie dans le corps d'une méthode, toutes les variables déclarées final dans le champ d'application de cette méthode sont accessible depuis la classe intérieure

une variable de classe statique doit exister dès le début de la JVM et doit être initialisée dans la classe. Le message d'erreur n'apparaîtra pas si vous faites cela.

183
répondu czupe 2014-02-17 01:28:43

le mot-clé final peut être interprété de deux façons différentes selon ce qu'il est utilisé sur:

types de valeur: pour int s, double s etc, il veillera à ce que la valeur ne peut pas changer,

types de référence: pour les références à des objets, final garantit que la référence ne changera jamais, ce qui signifie qu'elle fera toujours référence à la même objet. Il ne donne aucune garantie que les valeurs à l'intérieur de l'objet soient les mêmes.

en tant que tel, final List<Whatever> foo; garantit que foo se réfère toujours à la même Liste , mais le contenu de ladite liste peut changer avec le temps.

46
répondu Smallhacker 2017-07-31 02:51:15

si vous faites foo statique, vous devez l'initialiser dans la classe constructeur (ou inline où vous le définissez) comme les exemples suivants.

constructeur de la Classe (pas d'exemple):

private static final List foo;

static
{
   foo = new ArrayList();
}

Inline:

private static final List foo = new ArrayList();

le problème ici n'est pas de savoir comment fonctionne le modificateur final , mais plutôt comment fonctionne le modificateur static .

le modificateur final impose 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 que le code que vous avez défini pour le constructeur soit lancé, donc vous obtenez les résultats suivants:

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

lorsque vous n'initialisez pas un attribut en ligne, le modificateur final impose que vous l'initialisiez et que vous devez le faire dans le constructeur. Si vous avez également un static modificateur, le constructeur, 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'avez pas initialisé foo lorsque la classe est créée.

pensez au bloc static{} comme un constructeur pour un objet de type Class . C'est là que vous devez effectuer l'initialisation de votre static final attributs de classe (si non fait en ligne).

note de Côté:

le modificateur final assure la cohérence seulement 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 à cet objet, mais l'objet lui-même n'est pas constante.

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

22
répondu 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 immuable de l'objet.

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

2) un objet immuable est un objet dont l'état peut et non être modifié, mais dont la référence peut être changée. Ex:

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

ref la variable x peut être changée pour pointer une chaîne différente, mais la valeur de "abc" ne peut pas être changé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 à l'intérieur d'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 à l'intérieur d'une méthode.

5) A la 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 même avant. Vous devez donc assigner des valeurs à une variable statique pendant la déclaration elle-même.

6
répondu user892871 2014-09-26 07:34:59

mérite de mentionner quelques définitions simples:

Classes / Méthodes

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 dépassée par les sous-classes.

Variables

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

final essentiellement éviter de remplacer/remplacer par quoi que ce soit (sous-classes, variable" reassign"), selon le cas.

6
répondu ivanleoncz 2018-05-22 13:34:37

supposons que vous ayez deux boîtes à billets, rouge et blanc. Vous assignez ces boîtes à monnaie seulement deux enfants et ils ne sont pas autorisés à échanger leurs boîtes. Vous avez donc des boîtes aux lettres rouges ou blanches(finales) vous ne pouvez pas modifier la boîte mais vous pouvez mettre de l'argent sur votre boîte.Tout le monde s'en fout (Modification-2).

4
répondu 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, 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 String hello = "Hello";

lorsque nous utilisons le final mot-clé avec une déclaration variable, la valeur stockée à l'intérieur de ce la variable ne peut pas être changé dernier.

par exemple:

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

Note : une classe déclarée comme finale ne peut être étendue ni héritée (I. e, 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 supplantées par des sous-classes.

les avantages d'utiliser le mot-clé final sont abordés dans ce fil .

3
répondu Desta Haileselassie Hagos 2017-05-23 12:10:43

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

  1. variable
  2. méthode
  3. classe
  4. final final le mot-clé final peut être appliqué avec les variables, une variable final qui n'a aucune valeur, est appelé blanc final variable ou uninitialized final variable. Il peut être initialisé dans le constructeur. La variable vierge final peut être static qui sera initialisée dans le bloc static seulement.

    Java variable finale:

    si vous faites n'importe quelle variable comme final , vous ne peut pas changer la valeur de final variable(il sera constant).

    exemple de la variable final

    il y a une variable finale speedlimit, nous allons changer la valeur de cette variable, mais elle ne peut pas être changée parce que la variable finale une fois assignée une valeur ne peut jamais être changé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  
    

    Java finale de la classe:

    si vous faites n'importe quelle classe comme final , vous ne peut pas étendre il.

    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();  
          }  
      }  
    

    Java final de la méthode:

    si vous faites n'importe quelle méthode comme finale, vous ne peut pas remplacer il.

    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();  
       }  
    }  
    

    : http://www.javatpoint.com/final-keyword

3
répondu Ali Ziaee 2018-01-30 07:33:36

quand vous le faites final statique, 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
répondu 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 la seule initialisation 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 s'appuyer sur instanciation (alias appel au constructeur) pour initialiser foo car les champs statiques doivent être disponibles sans une 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 instanciation t à ce moment il n'y a pas instanciation de foo ce qui signifie qu'il n'a pas été initialisé ainsi foo est défini à la valeur par défaut pour tous les objets qui est null . À ce stade, je suppose que votre code lance un NullPointerException lorsque vous essayez d'ajouter un élément à la liste.

1
répondu Kevin Bowersox 2013-03-27 09:14:38
  1. comme la variable finale n'est pas statique, elle peut être initialisée dans le constructeur. Mais si vous le rendez statique, il ne peut pas être initialisé par le constructeur (parce que les constructeurs ne sont pas statiques).
  2. ajout à la liste ne devrait pas s'arrêter en rendant la liste finale. final ne fait que lier la référence à un objet particulier. Vous êtes libre de changer l'état de l'objet, mais non pas de l'objet lui-même.
1
répondu Ankit 2015-06-05 01:50:49

Lisez toutes les réponses.

il y a un autre cas d'utilisateur où le mot-clé final peut être utilisé i.e. 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 la variable qui ne doit pas être changée.

1
répondu Pritam Banerjee 2017-06-27 23:48:06

j'ai pensé écrire une réponse mise à jour et en profondeur ici.

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

  1. classes

Un final class signifie que non autre classe peut étendre que la classe finale. Quand Java Run Time ( JRE ) sait qu'une référence objet est en type d'une classe finale (disons F), il sait que la valeur de cette référence ne peut être en 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.

ainsi quand il exécute n'importe quelle méthode de cet objet, cette méthode n'a pas besoin pour être résolu au temps d'exécution en utilisant un 1519190920" Tableau virtuel . c'est à dire au moment de l'exécution le polymorphisme ne peut pas être appliquée. Donc le temps de course ne s'en préoccupe pas. 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 d'enfant qui étend cette classe ne peut pas remplacer que la ou les méthodes finales. Ainsi, le comportement de durée d'exécution dans ce scénario est également tout à fait identique avec le comportement précédent I mentionné pour les classes.

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

si l'une des catégories ci-dessus est final , cela signifie que la valeur est déjà finalisée, de sorte que la valeur ne peut pas être changé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 la méthode

void someMethod(final String s){
    s = someOtherString; //compile error
}

cela signifie simplement que 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, depuis JRE sait que les valeurs ne peuvent pas être changées, il charge toutes ces valeurs finalisées (de références finales) dans L1 cache . Parce qu'il n'a pas besoin à charge encore et encore de mémoire principale . Dans le cas contraire, il charge le cache L2 et le charge de temps en temps à partir de la mémoire principale. C'est aussi une amélioration de la performance.

donc dans tous les scénarios ci-dessus, quand nous n'avons pas spécifié le mot-clé final dans les endroits que nous pouvons utiliser, nous n'avons pas besoin de s'inquiéter, optimisations des compilateurs le fera pour nous. Il y a aussi beaucoup d'autres choses que les optimisations de compilateurs font pour nous. :)

1
répondu Supun Wijerathne 2018-01-14 04:00:49

surtout sont corrects. En outre, si vous ne voulez pas que d'autres créent des sous-classes à partir de votre classe, alors déclarez votre classe comme finale. Puis il devient le niveau de feuille de votre hiérarchie d'arbre de classe que personne ne peut étendre plus loin. C'est une bonne pratique d'éviter d'énormes hiérarchie de classes.

0
répondu Shehan Simen 2013-12-23 02:10:04

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

foo = new ArrayList();

foo est un objet (avec liste de types) donc c'est un type de référence , 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 pas changer la valeur of foo (qui, encore une fois, n'est qu'une référence à un emplacement de mémoire). Au lieu de cela, ils ajoutent juste des éléments dans cette localisation de mémoire référencée. Pour violer le mot-clé final , vous devriez essayer de réattribuer foo comme suit:

foo = new ArrayList();

Que serait vous donne une erreur de compilation.


maintenant, avec cela hors du chemin, pensez à ce qui se passe quand vous ajoutez le statique mot-clé.

quand vous N'avez pas le mot-clé statique, 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 correct.

cependant, quand vous avez le mot-clé statique, un seul foo existe en mémoire qui est associé à la classe. Si vous deviez créer deux ou plusieurs objets, le constructeur tenterait de réattribuez ce foo à chaque fois, violant le mot-clé final .

0
répondu Niko Bellic 2014-07-22 19:02:21

ci-dessous sont 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 qu'elle ne peut pas être liée de nouveau à 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 variable finale peut se voir attribuer une valeur plus tard (ce qui n'est pas obligatoire lorsqu'elle est déclarée), mais une seule fois.

classes finales Une classe finale ne peut pas être prolongé (hérité)

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

public class Main {
   public static void main(String 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() {  //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
répondu roottraveller 2016-10-07 06:26:44