Suche…


Einführung

Autoboxing ist die automatische Konvertierung, die der Java-Compiler zwischen primitiven Typen und den entsprechenden Objekt-Wrapper-Klassen vornimmt. Beispiel: Konvertierung von int -> Integer, double -> Double ... Wenn die Konvertierung anders verläuft, wird dies als Unboxing bezeichnet. In der Regel wird dies in Sammlungen verwendet, die nur Objekte enthalten können, wobei primitive Boxtypen erforderlich sind, bevor sie in der Sammlung festgelegt werden.

Bemerkungen

Autoboxing kann bei häufiger Verwendung in Ihrem Code zu Leistungsproblemen führen.

Int und Integer austauschbar verwenden

Wenn Sie generische Typen mit Dienstprogrammklassen verwenden, stellen Sie oft fest, dass Nummerntypen nicht sehr hilfreich sind, wenn sie als Objekttypen angegeben werden, da sie nicht ihren primitiven Gegenstücken entsprechen.

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

Glücklicherweise können Ausdrücke, die als int ausgewertet werden, anstelle einer Integer wenn sie benötigt wird.

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

Die ints.add(i); Aussage ist äquivalent zu:

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

Und behält Eigenschaften von Integer#valueOf z. B. dass die gleichen Integer Objekte von der JVM zwischengespeichert werden, wenn sie sich innerhalb des Zahlen-Caching-Bereichs befinden.

Das gilt auch für:

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

In mehrdeutigen Situationen ist jedoch Vorsicht geboten. Betrachten Sie den folgenden Code:

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

Die java.util.List Schnittstelle enthält sowohl ein remove(int index) ( List Interface - Methode) und remove(Object o) (Methode von vererbten java.util.Collection ). In diesem Fall findet kein Boxing statt und remove(int index) wird aufgerufen.

Ein weiteres Beispiel für ein merkwürdiges Verhalten von Java-Code, das durch die automatische Integierung von Autoboxen mit Werten im Bereich von -128 bis 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

Dies geschieht, weil der Operator >= implizit intValue() das int zurückgibt, während == die Referenzen und nicht die int Werte vergleicht.

Standardmäßig speichert Java Werte im Bereich [-128, 127] , sodass der Operator == funktioniert, da die Integers Werte in diesem Bereich auf dieselben Objekte verweisen, wenn ihre Werte gleich sind. Der maximale Wert des zwischengespeicherten Bereichs kann mit der JVM-Option -XX:AutoBoxCacheMax definiert werden. Wenn Sie das Programm mit -XX:AutoBoxCacheMax=1000 , wird der folgende Code also true -XX:AutoBoxCacheMax=1000 :

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

Boolesche Anweisung in if verwenden

Aufgrund des automatischen Unboxing kann ein Boolean in einer if Anweisung verwendet werden:

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

Das funktioniert auch für while , do while und die Bedingung in den for Anweisungen.

Wenn der Boolean NullPointerException null , wird eine NullPointerException in die Konvertierung geworfen.

Auto-Unboxing kann zu NullPointerException führen

Dieser Code kompiliert:

Integer arg = null;
int x = arg;

Es stürzt jedoch zur Laufzeit mit einer java.lang.NullPointerException in der zweiten Zeile ab.

Das Problem ist, dass ein primitives int keinen null kann.

Dies ist ein minimalistisches Beispiel, aber in der Praxis manifestiert es sich oft in komplexeren Formen. Die NullPointerException ist nicht sehr intuitiv und hilft oft bei der Suche nach solchen Fehlern.

Stellen Sie sicher, dass Autoboxing und Auto-Unboxing mit Vorsicht ausgeführt werden, und stellen Sie sicher, dass nicht gepackte Werte zur Laufzeit keine null haben.

Speicher- und Rechenaufwand für Autoboxing

Autoboxing kann einen erheblichen Speicheraufwand verursachen. Zum Beispiel:

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

verbraucht normalerweise eine beträchtliche Menge an Speicher (etwa 60 KB für 6 KB der tatsächlichen Daten).

Darüber hinaus erfordern Boxed Integer normalerweise zusätzliche Roundtrips im Speicher, wodurch die CPU-Caches weniger effektiv werden. In obigem Beispiel ist der Speicher, auf den zugegriffen wird, auf fünf verschiedene Speicherorte verteilt, die sich in ganz unterschiedlichen Speicherbereichen befinden können: 1. das HashMap Objekt, 2. das Map-Objekt Entry[] table , 3. das Entry Objekt, 4. das entrys key (Box der primitiven Schlüssel), 5. das entrys value Objekt (Box den Grundwertes).

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

Das Lesen von boxed erfordert zwei Speicherzugriffe, wobei nur einer auf das primitive zugreifen kann.

Beim Abrufen von Daten aus dieser Karte der scheinbar unschuldige Code

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

ist äquivalent zu:

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

Normalerweise verursacht der obige Code die Erstellung und Garbage Collection eines Integer Objekts für jede Map#get(Integer) -Operation. (Weitere Informationen finden Sie im Hinweis unten.)

Um diesen Aufwand zu reduzieren, bieten mehrere Bibliotheken optimierte Sammlungen für primitive Typen, für die kein Boxen erforderlich ist. Zusätzlich zum Vermeiden des Box-Overheads erfordert diese Sammlung etwa viermal weniger Speicher pro Eintrag. Java Hotspot kann zwar das Autoboxing durch Arbeiten mit Objekten auf dem Stapel statt mit dem Heap optimieren, es ist jedoch nicht möglich, den Speicheraufwand und die daraus resultierende Speicherumleitung zu optimieren.

Java 8-Streams verfügen außerdem über optimierte Schnittstellen für primitive Datentypen, z. B. IntStream , für die kein Boxen erforderlich ist.

Hinweis: Bei einer typischen Java-Laufzeitumgebung wird ein einfacher Cache aus Integer und anderen primitiven Wrapper-Objekten verwaltet, der von den Factory-Methoden von valueOf und von Autoboxing verwendet wird. Für Integer liegt der Standardbereich dieses Caches zwischen -128 und +127. Einige JVMs bieten eine JVM-Befehlszeilenoption zum Ändern der Cachegröße / des Cache-Bereichs.

Verschiedene Fälle, wenn Integer und Int austauschbar verwendet werden können

Fall 1: Bei Verwendung anstelle von Methodenargumenten.

Wenn für eine Methode ein Objekt der Wrapper-Klasse als Argument erforderlich ist, kann dem Argument eine Variable des jeweiligen primitiven Typs übergeben werden und umgekehrt.

Beispiel:

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

Fall 2: Beim Übergeben von Rückgabewerten:

Wenn eine Methode eine primitive Typvariable zurückgibt, kann ein Objekt der entsprechenden Wrapper-Klasse austauschbar als Rückgabewert und umgekehrt übergeben werden.

Beispiel:

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

Fall 3: Während der Durchführung von Operationen.

Bei jeder Ausführung von Operationen mit Zahlen können die primitive Typvariable und das Objekt der jeweiligen Wrapper-Klasse austauschbar verwendet werden.

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

Fallstricke : Denken Sie daran, ein Objekt der Wrapper-Klasse zu initialisieren oder einem Wert zuzuweisen.

Wenn Sie das Wrapper-Klassenobjekt und die primitive Variable austauschbar verwenden, vergessen Sie niemals, das Wrapper-Klassenobjekt zu initialisieren oder ihm einen Wert zuzuweisen. Andernfalls kann es zur Laufzeit zu einer Nullzeiger-Ausnahme kommen.

Beispiel:

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
    }

Im obigen Beispiel ist der Wert des Objekts nicht zugewiesen und nicht initialisiert, und daher wird das Programm zur Laufzeit in einer Nullzeiger-Ausnahme ausgeführt. Wie aus dem obigen Beispiel hervorgeht, sollte der Wert des Objekts niemals uninitialisiert und nicht zugewiesen bleiben.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow