Dans cet article, l'auteur vous présente une fonctionnalité très importante et intéressante de Java, qui est la boxe et le déballage automatiques, et explique les principes de la boxe et du déballage automatiques à partir du code source. Il reste un piège. Les développeurs peuvent facilement tomber dans ce piège s’ils ne font pas attention.
Autoboxing
Définition
Quand tout le monde écrit des programmes Java, ils définissent souvent un objet Integer de la manière suivante :
Integer i=100;
À partir du code ci-dessus, vous pouvez savoir que i est une référence de type Integer et que 100 est le type de données primitif en Java. Cette méthode permettant de transmettre directement un type de données de base à sa classe wrapper correspondante est appelée Autoboxing.
Dans jdk 1.5, l'autoboxing a été introduit pour la première fois. Avant jdk 1.5, si vous souhaitez définir un objet Integer avec une valeur de 100, vous devez faire ceci :
Integer i=new Integer (100);
Principe
Mettons un point d'arrêt au code ci-dessus « Integer i=100 ; » et suivons-le.
Ensuite, nous pouvons voir que le programme passe à la méthode valueOf(int i) de la classe Integer
/** * 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); }
En d'autres termes, la boxe signifie que jdk complète lui-même l'appel Integer.valueOf(100) pour vous.
Unboxing
Définition
Integer integer100=100; int int100=integer100;
À partir du code ci-dessus, vous pouvez voir que integer100 est une référence de type Integer , int100 est une primitive type de données de type int. Cependant, nous pouvons attribuer un objet de type Integer à une variable de son type de données primitif correspondant. C'est le déballage.
Le déballage et l'emballage sont des opérations opposées. La boxe consiste à attribuer un type de données primitif à une variable de la classe d'encapsulation correspondante. Le déballage consiste à attribuer une variable d'une classe de package à une variable du type de données primitif correspondant. L’emballage et le déballage portent également bien leur nom.
Principe
Je crois que tout le monde a deviné ce que jdk a fait pour nous pendant le processus de déballage. Prouvons notre conjecture par des expériences.
Mettez un point d'arrêt sur la deuxième ligne du code ci-dessus, c'est-à-dire placez un point d'arrêt sur "int int100=integer100;" et suivez-le.
Nous pouvons voir que le programme passe à la méthode intValue() de Integer.
/** * Returns the value of this <code>Integer</code> as an * <code>int</code>. */ public int intValue() { return value; }
Autrement dit, jdk nous aide à terminer l'appel à la méthode intValue(). Pour l'expérience ci-dessus, la méthode intValue() de integer100 est appelée et sa valeur de retour est affectée à int100.
Extension
Expérience 1
Integer integer400=400; int int400=400; System.out.println(integer400==int400);
Dans la troisième ligne du code ci-dessus, integer400 et int400 effectuent == opération. Ces deux types de variables sont différents. Integer400 est-il non encadré ou int400 encadré ? Quel est le résultat de l'opération ?
L'opération == consiste à déterminer si les adresses de deux objets sont égales ou si les valeurs de deux types de données de base sont égales. Par conséquent, il est facile pour tout le monde de spéculer que si integer400 est déballé, cela signifie que les valeurs des deux types de base sont comparées, et elles doivent être égales à ce moment-là, et le résultat courant est vrai si int400 est encadré, cela signifie que deux valeurs sont comparées. Que les adresses des objets soient égales, alors les adresses ne doivent pas être égales à ce moment et le résultat de l'opération est faux. (Quant à la raison pour laquelle l'auteur leur a attribué une valeur de 400, cela est lié au piège dont nous parlerons plus tard).
Notre résultat réel en cours d’exécution est vrai. Il s’agit donc du déballage d’entier400. Les résultats du suivi de code le prouvent également.
Expérience 2
Integer integer100=100; int int100=100; System.out.println(integer100.equals(int100));
Dans la troisième ligne du code ci-dessus, le paramètre de la méthode égale à entier100 est int100. Nous savons que le paramètre de la méthode equals est Object, et non le type de données de base, il doit donc être encadré ici par int100. Les résultats du suivi de code le prouvent également.
En fait, si le type de paramètre dans une méthode est un type de données primitif et que le type de paramètre transmis est sa classe d'encapsulation, il sera automatiquement déballé en conséquence, si le type de paramètre dans une méthode est une encapsulation ; class Type, le type de paramètre transmis est son type de données d'origine et il sera automatiquement encadré.
Expérience 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));
Dans la première expérience, nous avons appris que lorsqu'un type de données de base effectue une opération == avec une classe encapsulée, il déballera le classe encapsulée. Et si , -, *, /? Nous pouvons le découvrir dans cette expérience.
Si l'opération est effectuée, le type de données de base sera encadré, alors :
•Dans la ligne 4, integer100 int100 obtiendra un objet o de type Integer et de valeur 200, et exécutera toString () méthode de cet objet et sortie "200"
• Dans la ligne 5, integer100 int100 obtiendra un objet o de type Integer et de valeur 200, l'opération == comparera cet objet avec les objets Long200 sont comparés . Évidemment, false sera affiché ;
• À la ligne 6, integer100 int100 obtiendra un objet o de type Integer et de valeur 200. La méthode égale de Long compare long200 avec o , car les deux sont des types différents de classes d'encapsulation. , donc le résultat est faux ;
Si l'opération est effectuée, la classe d'encapsulation sera déballée, alors :
•第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<=IntegerCache.high时,直接返回IntegerCache.cache[i + 128]。其中,IntegerCache为Integer的内部静态类,其原码如下:
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'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得到是相同的对象。
对比扩展中的第二个实验,我们得知,当封装类与基础类型进行==运行时,封装类会进行拆箱,拆箱结果与基础类型对比值;而两个封装类进行==运行时,与其它的对象进行==运行一样,对比两个对象的地址,也即判断是否两个引用是否指向同一个对象。
以上这篇Une brève discussion sur le boxing et unboxing automatique Java et leurs pièges就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持PHP中文网。
更多Une brève discussion sur le boxing et unboxing automatique Java et leurs pièges相关文章请关注PHP中文网!