Sök…


Introduktion

Autoboxing är den automatiska omvandlingen som Java-kompilatorn gör mellan primitiva typer och deras motsvarande objektomslagsklasser. Exempel, konvertering av int -> heltal, dubbel -> dubbel ... Om konverteringen går åt andra hållet kallas detta unboxing. Vanligtvis används detta i samlingar som inte kan innehålla andra än objekt, där primitiva typer av boxning behövs innan de ställs in i samlingen.

Anmärkningar

Autoboxing kan ha prestandaproblem när de används ofta i din kod.

Använd int och heltal utbytbart

När du använder generiska typer med verktygsklasser kan du ofta upptäcka att numretyper inte är till stor hjälp när de anges som objekttyper, eftersom de inte är lika med deras primitiva motsvarigheter.

List<Integer> ints = new ArrayList<Integer>();
Java SE 7
List<Integer> ints = new ArrayList<>();

Lyckligtvis kan uttryck som utvärderar till int användas i stället för ett Integer när det behövs.

for (int i = 0; i < 10; i++)
    ints.add(i);

ints.add(i); uttalande motsvarar:

ints.add(Integer.valueOf(i));

Och behåller egenskaper från Integer#valueOf som att ha samma Integer objekt cachade av JVM när det ligger inom cacheintervallet.

Detta gäller också för:

  • byte och Byte
  • short och Short
  • float och Float
  • double och Double
  • long och Long
  • char och Character
  • boolean och Boolean

Man måste dock vara försiktig i tvetydiga situationer. Tänk på följande kod:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
ints.remove(1); // ints is now [1, 3]

Den java.util.List gränssnittet innehåller både en remove(int index) ( List gränssnittsmetoden) och en remove(Object o) (metod ärvs från java.util.Collection ). I detta fall sker ingen boxning och kallas remove(int index) .

Ytterligare ett exempel på konstigt Java-kodbeteende orsakat av autoboxande heltal med värden i området från -128 till 127 :

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c <= d); // true
System.out.println(c >= d); // true
System.out.println(c == d); // false

Detta händer eftersom >= operatör implicit kallar intValue() som returnerar int medan == jämför referenser , inte int värdena.

Som standard cachar Java värden i intervallet [-128, 127] , så operatören == fungerar eftersom Integers i detta intervall hänvisar till samma objekt om deras värden är desamma. Det maximala värdet för det cachebara området kan definieras med -XX:AutoBoxCacheMax JVM-alternativet. Så om du kör programmet med -XX:AutoBoxCacheMax=1000 kommer följande kod att skrivas ut true :

Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // true

Använda Boolean i if-uttalande

På grund av automatisk avbokning kan man använda en Boolean i ett if uttalande:

Boolean a = Boolean.TRUE;
if (a) { // a gets converted to boolean
    System.out.println("It works!");
}

Det fungerar en while , do while och villkoret i for uttalanden också.

Observera att om Boolean är null kommer en NullPointerException att kastas i konverteringen.

Auto-unboxing kan leda till NullPointerException

Den här koden sammanställer:

Integer arg = null;
int x = arg;

Men det kraschar vid körning med en java.lang.NullPointerException på den andra raden.

Problemet är att en primitiv int inte kan ha ett null .

Detta är ett minimalistiskt exempel, men i praktiken manifesteras det ofta i mer sofistikerade former. NullPointerException är inte särskilt intuitiv och är ofta liten hjälp för att hitta sådana buggar.

Lita på autoboxing och auto-unboxing med försiktighet, se till att värden för unboxed inte har null vid körning.

Memory and Computational Overhead of Autoboxing

Autoboxning kan komma att ha en betydande minneskostnad. Till exempel:

Map<Integer, Integer> square = new HashMap<Integer, Integer>();
for(int i = 256; i < 1024; i++) {
    square.put(i, i * i); // Autoboxing of large integers
}

kommer vanligtvis att konsumera en betydande mängd minne (cirka 60 kb för 6 k faktiska data).

Dessutom kräver boxade heltal vanligtvis ytterligare rundturer i minnet, och gör därför CPU-cachar mindre effektiva. I exemplet ovan sprids det HashMap minnet ut till fem olika platser som kan vara i helt olika regioner i minnet: 1. HashMap objektet, 2. kartans Entry[] table -tabellobjekt, 3. Entry objektet, 4. entrys key object (boxning av primitiv nyckel), 5. entrys value object (boxning av primitivt värde).

class Example {
  int primitive; // Stored directly in the class `Example`
  Integer boxed; // Reference to another memory location
}

boxed läsa boxed krävs två minnesåtkomstar, endast åtkomst till primitive .

När du hämtar data från den här kartan, den till synes oskyldiga koden

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(i);
}

är ekvivalent med:

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(Integer.valueOf(i)).intValue();
}

Vanligtvis orsakar ovanstående kod skapandet och skräpsamlingen av ett Integer objekt för varje Map#get(Integer) -operation. (Se not nedan för mer information.)

För att minska detta overhead erbjuder flera bibliotek optimerade samlingar för primitiva typer som inte kräver boxning. Förutom att undvika boxningskostnaderna kräver dessa samlingar cirka 4x mindre minne per post. Medan Java Hotspot kanske kan optimera autoboxningen genom att arbeta med objekt på stacken istället för högen, är det inte möjligt att optimera minnesomkostningen och resultera i indirekt minne.

Java 8-strömmar har också optimerade gränssnitt för primitiva datatyper, till exempel IntStream som inte kräver boxning.

Obs: en typisk Java-runtime upprätthåller en enkel cache för Integer och andra primitiva omslagobjekt som används av valueOf fabriksmetoderna och genom autoboxing. För Integer är standardintervallet för denna cache -128 till +127. Vissa JVM: er har ett JVM-kommandoradsalternativ för att ändra cache-storlek / intervall.

Olika fall när heltal och int kan användas omväxlande

Fall 1: När det används i stället för metodargument.

Om en metod kräver ett objekt av omslagsklass som argument. Därefter kan argumentet omväxlande ges en variabel av respektive primitivtyp och vice versa.

Exempel:

int i;
Integer j;
void ex_method(Integer i)//Is a valid statement
void ex_method1(int j)//Is a valid statement

Fall 2: När du returnerar värden:

När en metod returnerar en primitiv typvariabel, kan ett objekt i motsvarande omslagsklass passeras som retursvärdet utbytbart och vice versa.

Exempel:

int i;
Integer j;
int ex_method()
{...
return j;}//Is a valid statement
Integer ex_method1()
{...
return i;//Is a valid statement
}

Fall 3: När du utför operationer.

Varje gång man utför operationer på siffror kan variabeln av primitivtyp och objekt i respektive omslagsklass användas omväxlande.

int i=5;
Integer j=new Integer(7);
int k=i+j;//Is a valid statement
Integer m=i+j;//Is also a valid statement

Fallgrop : Kom ihåg att initialisera eller tilldela ett värde till ett objekt i omslagsklassen.

När du använder omslagsklassobjekt och primitiv variabel omväxlande kan du aldrig glömma eller missa att initialisera eller tilldela ett värde till omslagsklassobjektet annars kan det leda till undantag från nollpekaren vid körning.

Exempel:

public class Test{
    Integer i;
    int j;
    public void met()
    {j=i;//Null pointer exception
    SOP(j);
    SOP(i);}   
    public static void main(String[] args)
    {Test t=new Test();
    t.go();//Null pointer exception
    }

I exemplet ovan är värdet på objektet inte tilldelat och uninitialiserat och därför kommer programmet under körning att undvika nollpekareundantag. Så klart från exemplet ovan bör värdet på objekt aldrig lämnas oinitialiserat och otilldelat.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow