Zoeken…


Invoering

Autoboxing is de automatische conversie die Java-compiler tussen primitieve typen en hun overeenkomstige objectwikkelklassen uitvoert . Voorbeeld, het converteren van int -> Integer, dubbel -> Dubbel ... Als de conversie de andere kant op gaat, wordt dit unboxing genoemd. Meestal wordt dit gebruikt in collecties die niet anders dan objecten kunnen bevatten, waar primitieve typen boksen nodig is voordat u ze in de collectie plaatst.

Opmerkingen

Autoboxing kan prestatieproblemen veroorzaken wanneer het vaak in uw code wordt gebruikt.

Int en Integer door elkaar gebruiken

Aangezien u generieke typen met hulpprogramma-klassen gebruikt, merkt u vaak dat nummertypen niet erg nuttig zijn wanneer ze als objecttypen worden opgegeven, omdat ze niet gelijk zijn aan hun primitieve tegenhangers.

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

Gelukkig kunnen expressies die evalueren tot int worden gebruikt in plaats van een geheel Integer wanneer dat nodig is.

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

De ints.add(i); verklaring is gelijk aan:

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

En behoudt eigenschappen van Integer#valueOf zoals het hebben van dezelfde Integer objecten in de cache van de JVM wanneer deze zich binnen het bereik voor nummercache bevindt.

Dit geldt ook voor:

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

Voorzichtigheid is echter geboden in onduidelijke situaties. Overweeg de volgende code:

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

De java.util.List -interface bevat zowel een remove(int index) ( List -interface methode) en een remove(Object o) (methode overgenomen van java.util.Collection ). In dit geval vindt geen boksen plaats en wordt remove(int index) aangeroepen.

Nog een voorbeeld van vreemd Java-codegedrag veroorzaakt door autoboxing van gehele getallen met waarden in het bereik van -128 tot 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

Dit gebeurt omdat de operator >= impliciet intValue() aanroept die int retourneert terwijl == referenties vergelijkt, niet de int waarden.

Standaard slaat Java waarden op in bereik [-128, 127] , dus de operator == werkt omdat de Integers in dit bereik naar dezelfde objecten verwijzen als hun waarden hetzelfde zijn. De maximale waarde van het cachebereik kan worden gedefinieerd met de optie -XX:AutoBoxCacheMax JVM. Dus als u het programma uitvoert met -XX:AutoBoxCacheMax=1000 , wordt de volgende code true afgedrukt:

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

Boolean in if-instructie gebruiken

Vanwege automatisch unboxing kan men een Boolean in een if statement:

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

Dat werkt voor while , do while en de voorwaarde in de for statements ook.

Merk op dat als de Boolean null , een NullPointerException in de conversie wordt gegooid.

Auto-unboxing kan leiden tot NullPointerException

Deze code bestaat uit:

Integer arg = null;
int x = arg;

Maar het crasht tijdens runtime met een java.lang.NullPointerException op de tweede regel.

Het probleem is dat een primitieve int geen null kan hebben.

Dit is een minimalistisch voorbeeld, maar in de praktijk manifesteert het zich vaak in meer verfijnde vormen. De NullPointerException is niet erg intuïtief en biedt vaak weinig hulp bij het vinden van dergelijke bugs.

Vertrouw op autoboxing en auto-unboxing met zorg, zorg ervoor dat unboxed waarden niet zal hebben null -waarden tijdens de uitvoering.

Geheugen en computeroverhead bij autoboxing

Autoboxing kan zorgen voor een aanzienlijke geheugenoverhead. Bijvoorbeeld:

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

kost doorgaans een aanzienlijke hoeveelheid geheugen (ongeveer 60 kb voor 6 k werkelijke gegevens).

Bovendien vereisen integers in de box meestal extra round-trips in het geheugen, en maken daardoor CPU-caches minder effectief. In het bovenstaande voorbeeld is het geopende geheugen verspreid over vijf verschillende locaties die zich in geheel verschillende gebieden van het geheugen kunnen bevinden: 1. het HashMap object, 2. het HashMap Entry[] table , 3. het Entry object, 4. de entrys key object (boksen van de primitieve sleutel), 5. het entrys value object (boksen van de primitieve waarde).

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

boxed lezen vereist twee geheugentoegangen, toegang tot primitive slechts één.

Bij het ophalen van gegevens van deze kaart, de schijnbaar onschuldige code

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

is gelijk aan:

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

Meestal zorgt de bovenstaande code voor het maken en ophalen van een Integer object voor elke Map#get(Integer) bewerking. (Zie opmerking hieronder voor meer informatie.)

Om deze overhead te verminderen, bieden verschillende bibliotheken geoptimaliseerde collecties voor primitieve typen waarvoor geen boksen nodig is. Naast het vermijden van boksen overhead, vereist deze verzameling ongeveer 4x minder geheugen per invoer. Hoewel Java Hotspot mogelijk de autoboxing kan optimaliseren door te werken met objecten op de stapel in plaats van de heap, is het niet mogelijk om de overhead van het geheugen en de resulterende geheugenindirection te optimaliseren.

Java 8-streams hebben ook geoptimaliseerde interfaces voor primitieve gegevenstypen, zoals IntStream waarvoor geen boksen nodig is.

Opmerking: een typische Java-runtime onderhoudt een eenvoudige cache van Integer en een ander primitief wrapper-object dat wordt gebruikt door de valueOf fabrieksmethoden en door autoboxing. Voor Integer is het standaardbereik van deze cache -128 tot +127. Sommige JVM's bieden een JVM-opdrachtregeloptie voor het wijzigen van de cachegrootte / het bereik.

Verschillende gevallen waarin Integer en int door elkaar kunnen worden gebruikt

Geval 1: tijdens gebruik in plaats van methodeargumenten.

Als een methode een object van wrapper-klasse als argument vereist. Vervolgens kan het argument door een variabele van het respectieve primitieve type worden doorgegeven en vice versa.

Voorbeeld:

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

Geval 2: Bij het doorgeven van retourwaarden:

Wanneer een methode een variabele van het primitieve type retourneert, kan een object van de overeenkomstige wrapper-klasse worden doorgegeven als de retourwaarde uitwisselbaar en vice versa.

Voorbeeld:

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

Geval 3: Tijdens het uitvoeren van bewerkingen.

Wanneer bewerkingen op getallen worden uitgevoerd, kunnen de variabele van het primitieve type en het object van de respectieve wrapper-klasse door elkaar worden gebruikt.

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

Valkuil : vergeet niet een waarde van een wrapper-klasse te initialiseren of toe te wijzen.

Bij het gebruik van het wrapper class-object en de primitieve variabele door elkaar heen nooit vergeten of missen om een wrapper class-object te initialiseren of een waarde toe te kennen, anders kan dit tijdens runtime leiden tot een nulaanwijzeruitzondering.

Voorbeeld:

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
    }

In het bovenstaande voorbeeld is de waarde van het object niet toegewezen en niet geïnitialiseerd en loopt het programma dus tijdens runtime een uitzondering voor de aanwijzer.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow