Java >> Java tutorial >  >> Tag >> final

Effektivt endelig vs endelig - Forskellig adfærd

Først og fremmest taler vi om lokale variabler kun. Faktisk endelig gælder ikke for felter. Dette er vigtigt, da semantikken for final Felter er meget tydelige og er underlagt tunge kompilatoroptimeringer og hukommelsesmodelleløfter, se $ 17.5.1 på semantikken i de endelige felter.

På overfladeniveau final og effectively final for lokale variabler er faktisk identiske. JLS skelner dog klart mellem de to, hvilket faktisk har en bred vifte af effekter i særlige situationer som denne.

Forudsætning

Fra JLS§4.12.4 om final variabler:

En konstant variabel er en final variabel af primitiv type eller skriv String der er initialiseret med et konstant udtryk (§15.29). Om en variabel er en konstant variabel eller ej kan have konsekvenser med hensyn til klasseinitialisering (§12.4.1), binær kompatibilitet (§13.1), tilgængelighed (§14.22) og bestemt tildeling (§16.1.1).

Siden int er primitiv, variablen a er sådan en konstant variabel .

Yderligere fra samme kapitel om effectively final :

Visse variabler, der ikke erklæres endelige, betragtes i stedet som endelige:...

Så ud fra måden dette er formuleret på, er det klart, at i det andet eksempel, a er ikke betragtes som en konstant variabel, da den ikke er endelig , men kun faktisk endeligt.

Adfærd

Nu hvor vi har forskellen, lad os se, hvad der foregår, og hvorfor outputtet er anderledes.

Du bruger den betingede operator ? : her, så vi er nødt til at tjekke dens definition. Fra JLS§15.25:

Der er tre slags betingede udtryk, klassificeret efter det andet og tredje operandudtryk:boolske betingede udtryk , numeriske betingede udtryk , og referencebetingede udtryk .

I dette tilfælde taler vi om numeriske betingede udtryk , fra JLS§15.25.2:

Typen af ​​et numerisk betinget udtryk bestemmes som følger:

Og det er den del, hvor de to sager bliver klassificeret forskelligt.

effektivt endeligt

Den version, der er effectively final matches af denne regel:

Ellers generel numerisk forfremmelse (§5.6) anvendes på anden og tredje operand, og typen af ​​det betingede udtryk er den promoverede type af anden og tredje operand.

Hvilket er den samme adfærd, som hvis du ville gøre 5 + 'd' , dvs. int + char , hvilket resulterer i int . Se JLS§5.6

Numerisk forfremmelse bestemmer den promoverede type af alle udtryk i en numerisk kontekst. Den promoverede type er valgt således, at hvert udtryk kan konverteres til den promoverede type, og i tilfælde af en aritmetisk operation defineres operationen for værdier af den promoverede type. Rækkefølgen af ​​udtryk i en numerisk sammenhæng er ikke signifikant for numerisk fremme. Reglerne er som følger:

[...]

Dernæst udvidelse af primitiv konvertering (§5.1.2) og indsnævrende primitiv konvertering (§5.1.3) anvendes på nogle udtryk efter følgende regler:

I en numerisk valgsammenhæng gælder følgende regler:

Hvis et udtryk er af typen int og er ikke et konstant udtryk (§15.29), så er den promoverede type int , og andre udtryk, der ikke er af typen int gennemgå udvidende primitiv konvertering til int .

Så alt er forfremmet til int som a er en int allerede. Det forklarer outputtet af 97 .

endelig

Versionen med final variabel matches af denne regel:

Hvis en af ​​operanderne er af typen T hvor T er byte , short eller char , og den anden operand er et konstant udtryk (§15.29) af typen int hvis værdi kan repræsenteres i typen T , så er typen af ​​det betingede udtryk T .

Den endelige variabel a er af typen int og et konstant udtryk (fordi det er final ). Det kan repræsenteres som char , derfor er resultatet af typen char . Det afslutter outputtet a .

Eksempel på streng

Eksemplet med strengelighed er baseret på den samme kerneforskel, final variabler behandles som konstant udtryk/variabel og effectively final er ikke.

I Java, strenginternering er baseret på konstante udtryk, derfor

"a" + "b" + "c" == "abc"

er true så godt (brug ikke denne konstruktion i rigtig kode).

Se JLS§3.10.5:

Desuden refererer en streng bogstaveligt altid til den samme forekomst af klassen String. Dette skyldes, at strenge bogstaver - eller mere generelt , strenge, der er -værdierne for konstante udtryk (§15.29) - er "internet" for at dele unikke forekomster ved at bruge metoden String.intern (§12.5).

Let at overskue, da det primært taler om bogstaver, men det gælder faktisk også konstante udtryk.


Et andet aspekt er, at hvis variablen er erklæret endelig i metodens brødtekst, har den en anden adfærd end en endelig variabel, der sendes som parameter.

public void testFinalParameters(final String a, final String b) {
  System.out.println(a + b == "ab");
}

...
testFinalParameters("a", "b"); // Prints false

mens

public void testFinalVariable() {
   final String a = "a";
   final String b = "b";
   System.out.println(a + b == "ab");  // Prints true
}

...
testFinalVariable();

det sker fordi compileren ved det ved at bruge final String a = "a" a variabel vil altid have "a" værdi, så a og "a" kan ombyttes uden problemer. Anderledes, hvis a er ikke defineret final eller den er defineret final men dens værdi tildeles ved kørsel (som i eksemplet ovenfor, hvor finalen er a parameter) ved compileren ikke noget før dens brug. Så sammenkædningen sker under kørsel, og en ny streng genereres uden brug af intern pool.

Grundlæggende er adfærden:Hvis compileren ved, at en variabel er en konstant, kan du bruge den på samme måde som at bruge konstanten.

Hvis variablen ikke er defineret final (eller den er endelig, men dens værdi er defineret ved kørsel), er der ingen grund for compileren til at håndtere den som en konstant, også hvis dens værdi er lig med en konstant, og dens værdi aldrig ændres.


Java tag