(Turn) In-depth analysis of boxing and unboxing in Java

Transfer: https://www.cnblogs.com/dolphin0520/p/3780005.html

Deep dive into boxing and unboxing in Java

  The problem of automatic boxing and unboxing is a common problem in Java. Today we will take a look at some of the problems in boxing and unboxing. This article first describes the most basic things about packing and unpacking, and then takes a look at the problems related to packing and unpacking that are often encountered in the interview written test.

  Here is the table of contents outline for this article:

  1. What is packing? What is unboxing?

  2. How packing and unpacking are implemented

  3. Questions related to the interview

1. What is packing? What is unboxing?

  As mentioned in the previous article, Java provides a corresponding wrapper type for each basic data type. As for why a wrapper type is provided for each basic data type, I will not elaborate here. Interested friends can refer to the relevant material. Before Java SE5, if you wanted to generate an Integer object with a value of 10, you had to do it like this:

1
Integer i =  new  Integer( 10 );

  Since Java SE5 has provided the feature of automatic boxing, if you want to generate an Integer object with a value of 10, you only need to do this:

1
Integer i =  10 ;

  In this process, the corresponding Integer object is automatically created according to the value, which is boxing.

  So what is unboxing? As the name implies, it corresponds to boxing, which is to automatically convert the wrapper type to the basic data type:

1
2
Integer i =  10 ;   //装箱
int  n = i;    //拆箱

  Simply put, boxing is to automatically convert the basic data type to the wrapper type; unboxing is to automatically convert the wrapper type to the basic data type.

  The following table is the wrapper type corresponding to the basic data type:

int (4 bytes) Integer
byte (1 byte) Byte
short (2 bytes) Short
long (8 bytes) Long
float (4 bytes) Float
double (8 bytes) Double
char (2 bytes) Character
boolean (undecided) Boolean

2. How packing and unpacking are implemented

  After understanding the basic concepts of boxing in the previous section, let's take a look at how boxing and unboxing are implemented in this section.

  Let's take the Interger class as an example. Let's look at a piece of code:

1
2
3
4
5
6
7
public  class  Main {
     public  static  void  main(String[] args) {
         
         Integer i =  10 ;
         int  n = i;
     }
}

  After decompiling the class file, the following content is obtained:

  

  As can be seen from the bytecode content obtained by decompilation, the valueOf(int) method of Integer is automatically called during boxing. When unboxing, the intValue method of Integer is automatically called.

  Others are similar, such as Double, Character, and friends who don't believe it can try it manually.

  Therefore, the implementation process of boxing and unboxing can be summarized in one sentence:

  The boxing process is implemented by calling the valueOf method of the wrapper, and the unboxing process is implemented by calling the xxxValue method of the wrapper. (xxx represents the corresponding basic data type).

3. Questions related to the interview

  Although most people are clear about the concept of boxing and unboxing, they may not be able to answer the questions related to boxing and unboxing in interviews and written tests. Below are some common interview questions related to packing/unpacking.

1. What is the output of the following code?

1
2
3
4
5
6
7
8
9
10
11
12
public  class  Main {
     public  static  void  main(String[] args) {
         
         Integer i1 =  100 ;
         Integer i2 =  100 ;
         Integer i3 =  200 ;
         Integer i4 =  200 ;
         
         System.out.println(i1==i2);
         System.out.println(i3==i4);
     }
}

  Maybe some friends will say that they will output false, or some friends will say that they will output true. But in fact the output is:

true
false

   Why does this result occur? The output shows that i1 and i2 point to the same object, while i3 and i4 point to different objects. At this point, you only need to look at the source code to find out. The following code is the specific implementation of Integer's valueOf method:

public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }

  The implementation of the IntegerCache class is:

copy code
 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() {} }
copy code

  As can be seen from these two pieces of code, when creating an Integer object through the valueOf method, if the value is between [-128, 127], it returns a reference to an object that already exists in IntegerCache.cache; otherwise, a new Integer object is created .

  In the above code, the value of i1 and i2 is 100, so the existing objects will be taken directly from the cache, so i1 and i2 point to the same object, while i3 and i4 point to different objects respectively.

2. What is the output of the following code?

1
2
3
4
5
6
7
8
9
10
11
12
public  class  Main {
     public  static  void  main(String[] args) {
         
         Double i1 =  100.0 ;
         Double i2 =  100.0 ;
         Double i3 =  200.0 ;
         Double i4 =  200.0 ;
         
         System.out.println(i1==i2);
         System.out.println(i3==i4);
     }
}

  也许有的朋友会认为跟上面一道题目的输出结果相同,但是事实上却不是。实际输出结果为:

false
false

  至于具体为什么,读者可以去查看Double类的valueOf的实现。

  在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

  注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。

     Double、Float的valueOf方法的实现是类似的。

3.下面这段代码输出结果是什么:

1
2
3
4
5
6
7
8
9
10
11
12
public  class  Main {
     public  static  void  main(String[] args) {
         
         Boolean i1 =  false ;
         Boolean i2 =  false ;
         Boolean i3 =  true ;
         Boolean i4 =  true ;
         
         System.out.println(i1==i2);
         System.out.println(i3==i4);
     }
}

  输出结果是:

true
true

  至于为什么是这个结果,同样地,看了Boolean类的源码也会一目了然。下面是Boolean的valueOf方法的具体实现:

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE); }

  而其中的 TRUE 和FALSE又是什么呢?在Boolean中定义了2个静态成员属性:

copy code
 public static final Boolean TRUE = new Boolean(true); /** * The <code>Boolean</code> object corresponding to the primitive * value <code>false</code>. */ public static final Boolean FALSE = new Boolean(false);
copy code

  至此,大家应该明白了为何上面输出的结果都是true了。

4.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。

  当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:

  1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

  2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。

5.下面程序的输出结果是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  class  Main {
     public  static  void  main(String[] args) {
         
         Integer a =  1 ;
         Integer b =  2 ;
         Integer c =  3 ;
         Integer d =  3 ;
         Integer e =  321 ;
         Integer f =  321 ;
         Long g = 3L;
         Long h = 2L;
         
         System.out.println(c==d);
         System.out.println(e==f);
         System.out.println(c==(a+b));
         System.out.println(c.equals(a+b));
         System.out.println(g==(a+b));
         System.out.println(g.equals(a+b));
         System.out.println(g.equals(a+h));
     }
}

  Don't look at the output first, the reader thinks about what the output of this code is. It should be noted here that when the two operands of the "==" operator are references of the wrapper type, the comparison points to the same object, and if one of the operands is an expression (ie. contains arithmetic operations) compares values ​​(that is, triggers an automatic unboxing process). Also, for wrapper types, the equals method does not perform type conversions. After understanding these two points, the above output results are clear at a glance:

copy code
true
false
true
true true false true
copy code

  There is no doubt about the first and second output results. The third sentence will trigger the automatic unboxing process (the intValue method will be called) because a+b contains arithmetic operations, so they compare whether the values ​​are equal. For c.equals(a+b), the automatic unboxing process will be triggered first, and then the automatic boxing process will be triggered. That is to say, a+b will first call the intValue method, and after the value after the addition operation is obtained, it will call Integer.valueOf method, and then compare equals. The same is true for the latter, but pay attention to the results of the penultimate and last output (if the value is of type int, the boxing process calls Integer.valueOf; if it is of type long, the Long boxing call is called Long .valueOf method).

If you are in doubt about the specific execution process above, you can try to obtain the decompiled bytecode content for viewing.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325185464&siteId=291194637