Hoe werkt het “FINAL” -woord in Java? (Ik kan nog steeds een object wijzigen.)

In Java gebruiken we finaltrefwoord met variabelen om zijn waarden op te geven, mogen niet worden gewijzigd.
Maar ik zie dat u de waarde in de constructor / methoden van de klasse kunt wijzigen. Nogmaals, als de variabele staticis, is het een compilatiefout.

Hier is de 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);
  }
}

hierboven-code werkt prima en geen fouten.

Wijzig nu de variabele als static:

private static final List foo;

Nu is het een compilatiefout. Hoe werkt dit finalecht?


1, Autoriteit 100%

U bent altijd toegestaan ​​om initialiseert A finalvariabele. De compiler zorgt ervoor dat u het maar één keer kunt doen.

Houd er rekening mee dat beltemethoden op een object opgeslagen in een finalvariabele heeft niets te maken met de semantiek van final. Met andere woorden: finalis alleen over de referentie zelf, en niet over de inhoud van het object waarnaar wordt verwezen.

Java heeft geen concept van onmetelijkheid van het object; Dit wordt bereikt door het voorwerp zorgvuldig te ontwerpen, en is een ver van triviaal streven.


2, Autoriteit 108%

Dit is een favoriete interviewvraag. Met deze vraag probeert de interviewer erachter te komen hoe goed u het gedrag van objecten begrijpt met betrekking tot constructors, methoden, klassevariabelen (statische variabelen) en instantievariabelen.

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.
    }
}

In het bovenstaande geval hebben we een constructor voor ‘Test’ gedefinieerd en deze een ‘setFoo’-methode gegeven.

Over constructor:Constructor kan slechts éénkeer per objectcreatie worden aangeroepen door het trefwoord newte gebruiken. U kunt de constructor niet meerdere keren aanroepen, omdat de constructor daar niet voor is ontworpen.

Over methode:een methode kan zo vaak worden aangeroepen als je wilt (zelfs nooit) en de compiler weet het.

Scenario 1

private final List foo;  // 1

foois een instantievariabele. Wanneer we een klasseobject Testmaken, wordt de instantievariabele foogekopieerd in het object van de klasse Test. Als we foobinnen de constructor toewijzen, dan weet de compiler dat de constructor maar één keer wordt aangeroepen, dus er is geen probleem om deze binnen de constructor toe te wijzen.

Als we foobinnen een methode toewijzen, weet de compiler dat een methode meerdere keren kan worden aangeroepen, wat betekent dat de waarde meerdere keren moet worden gewijzigd, wat niet is toegestaan voor een finalvariabele. Dus de compiler besluit dat de constructor een goede keuze is! U kunt slechts één keer een waarde aan een laatste variabele toewijzen.

Scenario 2

private static final List foo = new ArrayList();

foois nu een statischevariabele. Wanneer we een instantie van de klasse Testmaken, wordt fooniet naar het object gekopieerd omdat foostatisch is. Nu is foogeen onafhankelijke eigenschap van elk object. Dit is een eigenschap van de klasse Test. Maar fookan worden gezien door meerdere objecten en als elk object dat is gemaakt met behulp van het newtrefwoord, dat uiteindelijk de Test-constructor zal aanroepen die verandert de waarde op het moment van het maken van meerdere objecten (Onthoud dat static fooniet in elk object wordt gekopieerd, maar wordt gedeeld tussen meerdere objecten.)

Scenario 3

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

Boven Modification-2komt van uw vraag. In het bovenstaande geval wijzigt u niet het eerste object waarnaar wordt verwezen, maar voegt u inhoud toe binnen foo, wat is toegestaan. Compiler klaagt als u probeert een new ArrayList()toe te wijzen aan de fooreferentievariabele.
RegelAls u een variabele finalheeft geïnitialiseerd, kunt u deze niet wijzigen om naar een ander object te verwijzen. (In dit geval ArrayList)

laatsteklassen kunnen niet worden gesubclasseerd
definitievemethoden kunnen niet worden overschreven. (Deze methode is in superklasse)
definitievemethoden kunnen worden overschreven. (Lees dit grammaticaal. Deze methode zit in een subklasse)


Antwoord 3, autoriteit 40%

Laatstezoekwoord kan op verschillende manieren worden gebruikt:

  • Een laatste klassekan niet worden gesubklasseerd.
  • Een laatste methodekan niet worden overschreven door subklassen
  • Een laatste variabelekan maar één keer worden geïnitialiseerd

Ander gebruik:

  • Wanneer een anonieme innerlijke klasse wordt gedefinieerd binnen de hoofdtekst van een methode,
    alle variabelen die definitief zijn verklaard in het kader van die methode zijn
    toegankelijk vanuit de innerlijke klasse

Een statische klassevariabele bestaat vanaf het begin van de JVM en moet in de klasse worden geïnitialiseerd. De foutmelding wordt niet weergegeven als u dit doet.


Antwoord 4, autoriteit 11%

Het finalzoekwoord kan op twee verschillende manieren worden geïnterpreteerd, afhankelijk van waarvoor het wordt gebruikt:

Waardetypes:Voor ints, doubles enz. zorgt het ervoor dat de waarde niet kan veranderen,

Referentietypen:voor verwijzingen naar objecten zorgt finalervoor dat de verwijzingnooit verandert, wat betekent dat deze altijd naar hetzelfde zal verwijzen object. Het geeft geen enkele garantie dat de waarden in het object waarnaar wordt verwezen, hetzelfde blijven.

Als zodanig, final List<Whatever> foo;zorgt ervoor dat fooaltijd verwijst naar dezelfdelijst, maar de inhoudvan die lijst kan in de loop van de tijd veranderen.


Antwoord 5, autoriteit 4%

Als je foostatisch maakt, moet je het initialiseren in de klassenconstructor (of inline waar je het definieert) zoals in de volgende voorbeelden.

Klasse-constructor (niet instantie):

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

Inline:

private static final List foo = new ArrayList();

Het probleem hier is niet hoe de finalmodifier werkt, maar eerder hoe de staticmodifier werkt.

De final-modifier dwingt een initialisatie van uw referentie af tegen de tijd dat de aanroep van uw constructor is voltooid (d.w.z. u moet deze initialiseren in de constructor).

Als je een attribuut inline initialiseert, wordt het geïnitialiseerd voordat de code die je voor de constructor hebt gedefinieerd wordt uitgevoerd, zodat je de volgende resultaten krijgt:

  • als foostaticis, wordt foo = new ArrayList()uitgevoerd vóór de static{}constructor die u voor uw klasse hebt gedefinieerd, wordt uitgevoerd
  • als fooniet staticis, wordt foo = new ArrayList()uitgevoerd voordat uw constructor wordt uitgevoerd

Als u een attribuut niet in-line initialiseert, dwingt de final-modifier af dat u het initialiseert en dat u dit in de constructor moet doen. Als je ook een staticmodifier hebt, is de constructor waarin je het attribuut moet initialiseren het initialisatieblok van de klasse: static{}.

De fout die u in uw code krijgt, is het feit dat static{}wordt uitgevoerd wanneer de klasse wordt geladen, vóór de tijd dat u een object van die klasse instantieert. Je hebt dus fooniet geïnitialiseerd wanneer de klas is gemaakt.

Denk aan het static{}blok als een constructor voor een object van het type Class. Hier moet u de initialisatie van uw klassekenmerken static finaluitvoeren (indien niet inline gedaan).

Kanttekening:

De finalmodifier verzekert alleen stabiliteit voor primitieve typen en referenties.

Als je een finalobject declareert, krijg je een finalverwijzingnaar dat object, maar het object zelf is niet constant.

Wat u werkelijk bereikt wanneer u een final-attribuut declareert, is dat, zodra u een object voor uw specifieke doel declareert (zoals de final Listdie u heeft gedeclareerd), dat en alleen dat object zal voor dat doel worden gebruikt: u kunt List fooniet wijzigen in een andere List, maar u kunt nog steeds uw Listdoor items toe te voegen/te verwijderen (de Listdie u gebruikt zal hetzelfde zijn, alleen met gewijzigde inhoud).


Antwoord 6, autoriteit 2%

Dit is een zeer goede vraag voor een sollicitatiegesprek. Soms vragen ze je zelfs wat het verschil is tussen een definitief object en een onveranderlijk object.

1) Wanneer iemand een definitief object noemt, betekent dit dat de verwijzing niet kan worden gewijzigd, maar de status (instantievariabelen) kan worden gewijzigd.

2) Een onveranderlijk object is een object waarvan de status nietkan worden gewijzigd, maar waarvan de referentie kan worden gewijzigd.
Bijv.:

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

ref variabele x kan worden gewijzigd om naar een andere tekenreeks te wijzen, maar de waarde van “abc” kan niet worden gewijzigd.

3) Instantievariabelen (niet-statische velden) worden geïnitialiseerd wanneer een constructor wordt aangeroepen. U kunt dus waarden initialiseren voor uw variabelen binnen een constructor.

4) “Maar ik zie dat je de waarde in de constructor/methoden van de klasse kunt wijzigen”. — Je kunt het niet binnen een methode veranderen.

5) Een statische variabele wordt geïnitialiseerd tijdens het laden van de klas. U kunt dus niet binnen een constructor initialiseren, het moet zelfs ervoor worden gedaan. U moet dus waarden toewijzen aan een statische variabele tijdensdeclaratie zelf.


Antwoord 7, autoriteit 2%

Het finaltrefwoord in java wordt gebruikt om de gebruiker te beperken. Het java finaltrefwoord kan in veel contexten worden gebruikt. Finale kan zijn:

  1. variabele
  2. methode
  3. klas

Het trefwoord finalkan worden toegepast met de variabelen, een final-variabele die geen waarde heeft, wordt lege final-variabele of niet-geïnitialiseerde finalvariabele. Het kan alleen in de constructor worden geïnitialiseerd. De lege variabele finalkan ook staticzijn en wordt alleen geïnitialiseerd in het static-blok.

Java laatste variabele:

Als u een variabele als finalmaakt, kunt u de waardevan de finalvariabele niet wijzigen (deze zal constant zijn).

Voorbeeld van finalvariabele

Er is een laatste variabele snelheidslimiet, we gaan de waarde van deze variabele wijzigen, maar deze kan niet worden gewijzigd omdat de uiteindelijke variabele die eenmaal een waarde heeft gekregen, nooit kan worden gewijzigd.

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 laatste les:

Als je een klasse als finalmaakt, kun je deze niet verlengen.

Voorbeeld van laatste les

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 laatste methode:

Als u een methode definitief maakt, kunt u deze niet overschrijven.

Voorbeeld van finalmethode
(run() in Honda kan run() in Bike niet overschrijven)

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

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


Antwoord 8, autoriteit 2%

Het is de moeite waard om enkele duidelijke definities te noemen:

Klassen/Methoden

Je kunt sommige of alle klassenmethoden declareren als final, om aan te geven dat de methode niet kan worden overschreven door subklassen.

Variabelen

Als een variabele finaleenmaal is geïnitialiseerd, bevat deze altijd dezelfde waarde.

finalvermijd in principe overschrijven/superschrijven door iets (subklassen, variabele “opnieuw toewijzen”), afhankelijk van het geval.


Antwoord 9

"A final variable can only be assigned once"

*Reflection*“wowo wacht, houd mijn biertje vast”.


Bevriezenvan finalvelden gebeurt in twee scenario’s:

  • Einde van constructor.
  • Als reflectie de waarde van het veld bepaalt. (zo vaak als het wil)

Laten we de wet overtreden

public class HoldMyBeer 
{
    final int notSoFinal;
    public HoldMyBeer()
    {
       notSoFinal = 1;
    }
    static void holdIt(HoldMyBeer beer, int yetAnotherFinalValue) throws Exception
    {
       Class<HoldMyBeer> cl = HoldMyBeer.class;
       Field field = cl.getDeclaredField("notSoFinal");
       field.setAccessible(true);
       field.set(beer, yetAnotherFinalValue);
    }
    public static void main(String[] args) throws Exception 
    {
       HoldMyBeer beer = new HoldMyBeer();
       System.out.println(beer.notSoFinal);
       holdIt(beer, 50);
       System.out.println(beer.notSoFinal);
       holdIt(beer, 100);
       System.out.println(beer.notSoFinal);
       holdIt(beer, 666);
       System.out.println(beer.notSoFinal);
       holdIt(beer, 8888);
       System.out.println(beer.notSoFinal);
    }    
}

Uitvoer:

1
50
100
666
8888

Het –finale “-veld is 5 verschillende ” definitief “-waarden toegewezen (Noteer de aanhalingstekens ) . En het kan verschillende waarden over en over hebben toegewezen.

Waarom? Omdat reflectie is als Chuck Norris, en als het de waarde van een geïnitialiseerde laatste veld wil wijzigen, dan is dit. Sommigen zeggen dat hij zelf degene is die de nieuwe waarden in de stapel duwt:

Code:
   7: astore_1
  11: aload_1
  12: getfield                
  18: aload_1
  19: bipush        50        //wait what
  27: aload_1
  28: getfield                
  34: aload_1
  35: bipush        100       //come on...
  43: aload_1
  44: getfield                
  50: aload_1
  51: sipush        666      //...you were supposed to be final...
  60: aload_1
  61: getfield                
  67: aload_1
  68: sipush        8888     //ok i'm out whatever dude
  77: aload_1
  78: getfield                

10

finalis een gereserveerd zoekwoord in Java om de gebruiker te beperken en deze kan worden toegepast op ledenvariabelen, methoden, klasse en lokale variabelen. Definitieve variabelen worden vaak gedeclareerd met de statictrefwoord in Java en worden als constanten behandeld. Bijvoorbeeld:

public static final String hello = "Hello";

Wanneer we de finaltrefwoord gebruiken met een variabele aangifte, kan de waarde die is opgeslagen in die variabele niet wordt gewijzigd.

Bijvoorbeeld:

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

Opmerking: een klasse die als definitief is gedeclareerd, kan niet worden uitgebreid of geërfd (d.w.z. er kan geen subklasse van de superklasse zijn). Het is ook goed om op te merken dat methoden die als definitief zijn gedeclareerd, niet kunnen worden overschreven door subklassen.

De voordelen van het gebruik van het laatste zoekwoord worden behandeld indeze thread.


Antwoord 11

Stel dat je twee spaarpotten hebt, rood en wit. Je wijst aan deze spaarpotten maar twee kinderen toe en ze mogen hun spaarpot niet verwisselen. Dus je hebt rode of witte spaarpotten (definitief) je kunt de doos niet wijzigen, maar je kunt wel geld op je doos zetten. Het kan niemand iets schelen (Modificatie-2).


Antwoord 12

Lees alle antwoorden.

Er is nog een andere gebruikerssituatie waarbij het trefwoord finalkan worden gebruikt, bijvoorbeeld in een methode-argument:

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

Kan worden gebruikt voor een variabele die niet mag worden gewijzigd.


Antwoord 13

Als je het statisch definitief maakt, moet het worden geïnitialiseerd in een statisch initialisatieblok

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

Antwoord 14

De finalTrefwoord geeft aan dat een variabele slechts één keer kan worden geïnitialiseerd. In uw code voert u alleen één initialisatie van de finale uit, zodat de voorwaarden zijn voldaan. Deze verklaring voert de eenzame initialisatie uit van foo. Merk op dat final! = Onveranderlijk, het betekent alleen dat de referentie niet kan veranderen.

foo = new ArrayList();

Wanneer u fooals static finalDe variabele moet worden geïnitialiseerd wanneer de klasse is geladen en kan niet vertrouwen op instantiatie (aka call to constructor) om fooOmdat statische velden beschikbaar moeten zijn zonder een instantie van een klasse. Er is geen garantie dat de constructeur is gebeld voorafgaand aan het gebruik van het statische veld.

Wanneer u uw methode onder de static final-cenario uitvoert, wordt de TestKlasse geladen vóór het instantiëren van tOp dit moment is er geen instantiatie van foowat betekent dat het niet is geïnitialiseerd, dus foois ingesteld op de standaard voor alle objecten die nullis. Op dit punt neem ik aan dat uw code een NullPointerExceptiongooit wanneer u probeert een item aan de lijst toe te voegen.


15

Allereerst, de plaats in uw code waar u initialiseert (d.w.z. toewijzen voor de eerste keer) Foo is hier:

foo = new ArrayList();

foo is een object (met typelijst), dus het is een -referentie type, geen -waarde type (zoals INT). Als zodanig heeft het een verwijzing naar een geheugenlocatie (bijvoorbeeld 0xa7d2a834) waar uw lijstelementen zijn opgeslagen. Lijnen zoals deze

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

wijzig de waarde van foo niet (wat wederom slechts een verwijzing is naar een geheugenlocatie). In plaats daarvan voegen ze gewoon elementen toe aan die geheugenlocatie waarnaar wordt verwezen. Als u het zoekwoord laatstewilt schenden, moet u foo opnieuw als volgt opnieuw toewijzen:

foo = new ArrayList();

Dat zoueen compilatiefout opleveren.


Nu, met dat uit de weg, denk eens na over wat er gebeurt als u het statischezoekwoord toevoegt.

Als je het statische sleutelwoord NIET hebt, heeft elk object dat de klasse instantieert zijn eigen kopie van foo. Daarom wijst de constructor een waarde toe aan een lege, nieuwe kopie van de foo-variabele, wat prima is.

Als je echter wel het statische sleutelwoord hebt, bestaat er maar één foo in het geheugen dat aan de klasse is gekoppeld. Als u twee of meer objecten zou maken, zou de constructor elke keer proberen die ene foo opnieuw toe te wijzen, in strijd met het laatstetrefwoord.


Antwoord 16

  1. Omdat de laatste variabele niet-statisch is, kan deze worden geïnitialiseerd in de constructor. Maar als je het statisch maakt, kan het niet worden geïnitialiseerd door de constructor (omdat constructors niet statisch zijn).
  2. Toevoeging aan lijst zal naar verwachting niet stoppen met het definitief maken van de lijst. finalbindt alleen de verwijzing naar een bepaald object. Je bent vrij om de ‘status’ van dat object te veranderen, maar niet het object zelf.

Antwoord 17

Hier volgen verschillende contexten waarin final wordt gebruikt.

Definitieve variabelenEen laatste variabele kan maar één keer worden toegewezen. Als de variabele een referentie is, betekent dit dat de variabele niet opnieuw kan worden gekoppeld om naar een ander object te verwijzen.

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
   }
}

Definitieve variabele kan later worden toegewezen (niet verplicht om een ​​waarde toe te kennen wanneer deze wordt gedeclareerd), maar slechts één keer.

Eindklassen Een laatste klasse kan niet worden uitgebreid (geërfd)

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

Eindmethoden Een definitieve methode kan niet worden overschreven door subklassen.

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

Antwoord 18

Vooral zijn correct. Verder, als je niet wilt dat anderen subklassen van je klas maken, verklaar je klas dan als definitief. Dan wordt het het bladniveau van uw klassenboomhiërarchie dat niemand het verder kan uitbreiden. Het is een goede gewoonte om een enorme hiërarchie van klassen te vermijden.

Other episodes