Heim  >  Artikel  >  Java  >  Eine kurze Diskussion über das automatische Boxen und Unboxen von Java und ihre Fallstricke

Eine kurze Diskussion über das automatische Boxen und Unboxen von Java und ihre Fallstricke

高洛峰
高洛峰Original
2017-01-16 15:51:271196Durchsuche

In diesem Artikel stellt Ihnen der Autor eine sehr wichtige und interessante Funktion in Java vor, nämlich das automatische Boxen und Unboxen, und erklärt gleichzeitig die Prinzipien des automatischen Boxens und Unboxens aus dem Quellcode Es bleibt eine Falle übrig. Entwickler können leicht in diese Falle tappen, wenn sie nicht aufpassen.

Autoboxing

Definition

Wenn jeder Java-Programme schreibt, definiert er ein Integer-Objekt oft auf die folgende Weise:

Integer i=100;

Aus dem obigen Code können Sie erkennen, dass i eine Referenz vom Typ Integer ist und 100 der primitive Datentyp in Java ist. Diese Methode zur direkten Übergabe eines Basisdatentyps an die entsprechende Wrapper-Klasse wird Autoboxing genannt.

In JDK 1.5 wurde Autoboxing erstmals eingeführt. Wenn Sie vor JDK 1.5 ein Integer-Objekt mit einem Wert von 100 definieren möchten, müssen Sie Folgendes tun:

Integer i=new Integer (100);

Prinzip

Setzen wir im obigen Code einen Haltepunkt bei „Integer i=100;“ und verfolgen ihn.

Eine kurze Diskussion über das automatische Boxen und Unboxen von Java und ihre Fallstricke

Als nächstes können wir sehen, dass das Programm zur valueOf(int i)-Methode der Integer-Klasse springt

/**
   * Returns a <tt>Integer</tt> instance representing the specified
   * <tt>int</tt> value.
   * If a new <tt>Integer</tt> instance is not required, this method
   * should generally be used in preference to the constructor
   * {@link #Integer(int)}, as this method is likely to yield
   * significantly better space and time performance by caching
   * frequently requested values.
   *
   * @param i an <code>int</code> value.
   * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
   * @since 1.5
   */
  public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
      return IntegerCache.cache[i + 128];
    else
      return new Integer(i);
  }

Mit anderen Worten bedeutet Boxen, dass JDK selbst den Aufruf Integer.valueOf(100) für Sie ausführt.

Unboxing

Definition

Integer integer100=100;
int int100=integer100;

Aus dem obigen Code können Sie ersehen, dass integer100 eine Referenz vom Typ Integer und int100 ein Grundelement ist Datentyp vom Typ int. Wir können jedoch ein Objekt vom Typ Integer einer Variablen des entsprechenden primitiven Datentyps zuweisen. Das ist Unboxing.

Auspacken und Verpacken sind gegensätzliche Vorgänge. Beim Boxen wird einer Variablen der entsprechenden Kapselungsklasse ein primitiver Datentyp zugewiesen. Beim Unboxing wird eine Variable einer Paketklasse einer Variablen des entsprechenden primitiven Datentyps zugewiesen. Auch Packen und Auspacken werden treffend benannt.

Prinzip

Ich glaube, jeder hat erraten, was jdk während des Unboxing-Prozesses für uns getan hat. Lassen Sie uns unsere Vermutung durch Experimente beweisen.

Setzen Sie einen Haltepunkt in die zweite Zeile des obigen Codes, d. h. setzen Sie einen Haltepunkt auf „int int100=integer100;“ und verfolgen Sie ihn.

Wir können sehen, dass das Programm zur intValue()-Methode von Integer springt.

/**
   * Returns the value of this <code>Integer</code> as an
   * <code>int</code>.
   */
  public int intValue() {
    return value;
  }

Das heißt, jdk hilft uns, den Aufruf der intValue()-Methode abzuschließen. Für das obige Experiment wird die intValue()-Methode von integer100 aufgerufen und ihr Rückgabewert int100 zugewiesen.

Erweiterung

Experiment 1

Integer integer400=400;
int int400=400;
System.out.println(integer400==int400);

In der dritten Zeile des obigen Codes führen integer400 und int400 die ==-Operation aus. Bei diesen beiden handelt es sich um unterschiedliche Arten von Variablen. Ist integer400 unboxed oder int400 boxed? Was ist das Ergebnis der Operation?

Mit der Operation == wird ermittelt, ob die Adressen zweier Objekte gleich sind oder ob die Werte zweier grundlegender Datentypen gleich sind. Daher ist es für jeden leicht zu spekulieren, dass, wenn integer400 unboxed ist, dies bedeutet, dass die Werte der beiden Grundtypen zu diesem Zeitpunkt gleich sein müssen und das laufende Ergebnis wahr ist, wenn int400 boxed ist. Dies bedeutet, dass zwei Werte verglichen werden. Unabhängig davon, ob die Adressen der Objekte gleich sind, dürfen die Adressen zu diesem Zeitpunkt nicht gleich sein und das Operationsergebnis ist falsch. (Der Grund, warum der Autor ihnen einen Wert von 400 zugewiesen hat, hängt mit der Falle zusammen, die später besprochen wird.)

Unser tatsächliches Laufergebnis ist wahr. Es handelt sich also um ein Integer400-Unboxing. Dies belegen auch die Ergebnisse des Code-Trackings.

Experiment 2

Integer integer100=100;
int int100=100;
System.out.println(integer100.equals(int100));

In der dritten Zeile des obigen Codes ist der Parameter der Methode equal of integer100 int100. Wir wissen, dass der Parameter der Methode „equals“ „Object“ und nicht der Basisdatentyp ist, daher muss er hier in der Box „int100“ sein. Dies belegen auch die Ergebnisse des Code-Trackings.

Wenn der Parametertyp in einer Methode tatsächlich ein primitiver Datentyp ist und der übergebene Parametertyp seine Kapselungsklasse ist, wird er entsprechend automatisch entpackt, wenn der Parametertyp in einer Methode eine Kapselung ist Klassentyp, der übergebene Parametertyp ist sein ursprünglicher Datentyp und wird automatisch eingerahmt.

Experiment 3

Integer integer100 = 100;
int int100 = 100;
Long long200 = 200l;
System.out.println(integer100 + int100);
System.out.println(long200 == (integer100 + int100));
System.out.println(long200.equals(integer100 + int100));

Im ersten Experiment haben wir gelernt, dass, wenn ein Basisdatentyp eine ==-Operation mit einer gekapselten Klasse ausführt, die Box entpackt wird gekapselte Klasse. Was wäre, wenn +, -, *, /? Das können wir in diesem Experiment herausfinden.

Wenn die +-Operation den Basisdatentyp umrahmt, dann:

•In Zeile 4 erhält integer100+int100 ein Objekt o vom Typ Integer und dem Wert 200 und führt toString( )-Methode dieses Objekts und Ausgabe „200“;

• In Zeile 5 ruft integer100+int100 ein Objekt o vom Typ Integer und den Wert 200 ab, und die Operation == vergleicht dieses Objekt mit dem long200-Objekt . Offensichtlich wird false ausgegeben;

• In Zeile 6 erhält „Integer100+Int100“ ein Objekt o vom Typ „Integer“ und den Wert „200“, und die Methode „equals“ von Long vergleicht „long200“ mit „o“, da es sich bei beiden um unterschiedliche Typen handelt Bei gekapselten Klassen wird „false“ ausgegeben.

Wenn die +-Operation die gekapselte Klasse entpackt, dann:

•第4行中,integer100+int100就会得到一个类型为int且value为200的基础数据类型b,再将b进行装箱得到o,执行这个对象的toString()方法,并输出”200”;

•第5行中,integer100+int100就会得到一个类型为int且value为200的基础数据类型b1,==运算将long200进行拆箱得到b2,显然b1==b2,输出true;

•第6行中,integer100+int100就会得到一个类型为int且value为200的基础数据类型b,Long的equals方法将b进行装箱,但装箱所得到的是类型为Integer的对象o,因为o与long200为不同的类型的对象,所以输出false;

程序运行的结果为:     

200
true
false

因而,第二种推测是正确,即在+运算时,会将封装类进行拆箱。

陷阱

陷阱1

Integer integer100=null;
int int100=integer100;

这两行代码是完全合法的,完全能够通过编译的,但是在运行时,就会抛出空指针异常。其中,integer100为Integer类型的对象,它当然可以指向null。但在第二行时,就会对integer100进行拆箱,也就是对一个null对象执行intValue()方法,当然会抛出空指针异常。所以,有拆箱操作时一定要特别注意封装类对象是否为null。

陷阱2

Integer i1=100;
Integer i2=100;
Integer i3=300;
Integer i4=300;
System.out.println(i1==i2);
System.out.println(i3==i4);

因为i1、i2、i3、i4都是Integer类型的,所以我们想,运行结果应该都是false。但是,真实的运行结果为“System.out.println(i1==i2);”为 true,但是“System.out.println(i3==i4);”为false。也就意味着,i1与i2这两个Integer类型的引用指向了同一个对象,而i3与i4指向了不同的对象。为什么呢?不都是调用Integer.valueOf(int i)方法吗?

让我们再看看Integer.valueOf(int i)方法。

/**
   * Returns a <tt>Integer</tt> instance representing the specified
   * <tt>int</tt> value.
   * If a new <tt>Integer</tt> instance is not required, this method
   * should generally be used in preference to the constructor
   * {@link #Integer(int)}, as this method is likely to yield
   * significantly better space and time performance by caching
   * frequently requested values.
   *
   * @param i an <code>int</code> value.
   * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
   * @since 1.5
   */
  public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
      return IntegerCache.cache[i + 128];
    else
      return new Integer(i);
  }

   

我们可以看到当i>=-128且i

private static class IntegerCache {
    static final int high;
    static final Integer cache[];
 
    static {
      final int low = -128;
 
      // high value may be configured by property
      int h = 127;
      if (integerCacheHighPropValue != null) {
        // Use Long.decode here to avoid invoking methods that
        // require Integer&#39;s autoboxing cache to be initialized
        int i = Long.decode(integerCacheHighPropValue).intValue();
        i = Math.max(i, 127);
        // Maximum array size is Integer.MAX_VALUE
        h = Math.min(i, Integer.MAX_VALUE - -low);
      }
      high = h;
 
      cache = new Integer[(high - low) + 1];
      int j = low;
      for(int k = 0; k < cache.length; k++)
        cache[k] = new Integer(j++);
    }
 
    private IntegerCache() {}
  }

我们可以清楚地看到,IntegerCache有静态成员变量cache,为一个拥有256个元素的数组。在IntegerCache中也对cache进行了初始化,即第i个元素是值为i-128的Integer对象。而-128至127是最常用的Integer对象,这样的做法也在很大程度上提高了性能。也正因为如此,“Integeri1=100;Integer i2=100;”,i1与i2得到是相同的对象。

对比扩展中的第二个实验,我们得知,当封装类与基础类型进行==运行时,封装类会进行拆箱,拆箱结果与基础类型对比值;而两个封装类进行==运行时,与其它的对象进行==运行一样,对比两个对象的地址,也即判断是否两个引用是否指向同一个对象。

以上这篇Eine kurze Diskussion über das automatische Boxen und Unboxen von Java und ihre Fallstricke就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持PHP中文网。

更多Eine kurze Diskussion über das automatische Boxen und Unboxen von Java und ihre Fallstricke相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn