In this article, the author introduces to you a very important and interesting feature in Java, which is automatic boxing and unboxing, and interprets the principles of automatic boxing and unboxing from the source code. At the same time, this feature also There is a trap left. Developers can easily fall into this trap if they are not careful.
Autoboxing
Definition
When everyone writes Java programs, they often define an Integer object in the following way:
Integer i=100;
From the above code, you can know that i is a reference of Integer type, and 100 is the primitive data type in Java. This method of directly passing a basic data type to its corresponding wrapper class is called Autoboxing.
In jdk 1.5, autoboxing was introduced for the first time. Before jdk 1.5, if you want to define an Integer object with a value of 100, you need to do this:
Integer i=new Integer (100);
Principle
Let's put a breakpoint in the above code "Integer i=100;" and track it.
Next, we can see that the program jumps to the valueOf(int i) method of the Integer class
/** * 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); }
In other words, boxing means that jdk itself completes the call Integer.valueOf(100) for you.
Unboxing
Definition
Integer integer100=100; int int100=integer100;
From the above code, you can see that integer100 is a reference of type Integer, int100 It is a primitive data type of type int. However, we can assign an object of type Integer to a variable of its corresponding primitive data type. This is unboxing.
Unboxing and boxing are opposite operations. Boxing is assigning a primitive data type to a variable of the corresponding encapsulating class. Unboxing is to assign a variable of a package class to a variable of the corresponding primitive data type. Packing and unboxing are also named quite aptly.
Principle
The author believes that everyone has guessed what jdk did for us during the unboxing process. Let's prove our conjecture through experiments.
Put a breakpoint on the second line of the above code, that is, put a breakpoint on "int int100=integer100;" and track it.
We can see that the program jumps to the intValue() method of Integer.
/** * Returns the value of this <code>Integer</code> as an * <code>int</code>. */ public int intValue() { return value; }
That is, jdk helps us complete the call to the intValue() method. For the above experiment, the intValue() method of integer100 is called and its return value is assigned to int100.
Extension
Experiment 1
Integer integer400=400; int int400=400; System.out.println(integer400==int400);
In the third line of the above code, integer400 and int400 perform == operation. These two are different types of variables. Is integer400 unboxed or int400 boxed? What is the result of the operation?
The == operation is to determine whether the addresses of two objects are equal or whether the values of two basic data types are equal. Therefore, it is easy for everyone to speculate that if integer400 is unboxed, it means that the values of two basic types are compared, and then they must be equal at this time, and the running result is true; if int400 is boxed, it means that two values are compared. Whether the addresses of the objects are equal, then the addresses must not be equal at this time, and the operation result is false. (As for why the author assigned them a value of 400, it is related to the trap that will be discussed later).
Our actual running result is true. So it is integer400 unboxing. The results of code tracking also prove this.
Experiment 2
Integer integer100=100; int int100=100; System.out.println(integer100.equals(int100));
In the third line of the above code, the parameter of the method equals of integer100 is int100. We know that the parameter of the equals method is Object, not the basic data type, so it must be int100 boxed here. The results of code tracking also prove this.
In fact, if the parameter type in a method is a primitive data type and the parameter type passed in is its encapsulation class, it will be automatically unboxed; accordingly, if the parameter type in a method is an encapsulation class Type, the parameter type passed in is its original data type, and it will be boxed automatically.
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));
In the first experiment, we have learned that when a basic data type performs a == operation with an encapsulated class, the encapsulated class for unboxing. What if +, -, *, /? We can find out in this experiment.
If the + operation will box the basic data type, then:
•In line 4, integer100+int100 will get an object o of type Integer and value 200, And execute the toString() method of this object, and output "200";
•In line 5, integer100+int100 will get an object o of type Integer and value 200, and the == operation will Compare this object with the long200 object. Obviously, false will be output;
•In line 6, integer100+int100 will get an object o of type Integer and value 200, and the equals method of Long will Comparing long200 with o, because both are different types of encapsulation classes, they output false;
If the + operation is performed, the encapsulation class will be unboxed, then:
•第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得到是相同的对象。
对比扩展中的第二个实验,我们得知,当封装类与基础类型进行==运行时,封装类会进行拆箱,拆箱结果与基础类型对比值;而两个封装类进行==运行时,与其它的对象进行==运行一样,对比两个对象的地址,也即判断是否两个引用是否指向同一个对象。
以上这篇A brief discussion on Java automatic boxing and unboxing and their pitfalls就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持PHP中文网。
更多A brief discussion on Java automatic boxing and unboxing and their pitfalls相关文章请关注PHP中文网!