1. Basic introduction of unboxing and packing
Boxing and unboxing are two useful syntactic sugars provided in Java.
Boxing refers to the automatic conversion of a basic data type to its wrapper type. Such as the conversion of int to Integer.
Unboxing refers to converting the wrapper type to the corresponding basic data type. Such as the conversion of Integer to int.
The following is an example:
Integer num1 = 1000;
int num2 = num1;
Among them, num1 is an object of type Integer, and the assignment operation to it here is a boxing process.
num2 is a variable of the basic type int, and assigning a value to it with num1 is an unboxing process.
2. Bytecode implementation of unboxing and boxing
Let's take a look at how boxing and unboxing are implemented from the perspective of bytecode.
So how to get the bytecode of Java code? In fact, the class file we get after compiling the Java source code with javac is its bytecode file.
But this class file exists in binary form and cannot be read directly. So we need another command javap to help us parse the class file into a readable form .
We save the following code in the test.java file (category, main method, etc. are omitted here):
Integer num1 = 1000;
int num2 = num1;
Use of javap command
Then use javac to compile, and then use javap -v to decompile. With javap plus the -v parameter, you can see more comprehensive information.
javac test.java
javap -v test
After using javap to decompile , we can get the following results:
0: sipush 1000
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: aload_1
8: invokevirtual #3// Method java/lang/Integer.intValue:()I
11: istore_2
The decompilation result here contains 6 bytecode instructions. If you understand these instructions, you will also understand the principle of a common unboxing and boxing implementation.
Since these instructions correspond to various operations on the stack frame, we first review the concept of the stack frame.
Stack frame review
I believe everyone is familiar with the Java virtual machine stack in the Java runtime data area .
The process of each Java method from invocation to completion of execution corresponds to the process of pushing a stack frame into and out of the virtual machine stack.
The execution process of the method is to perform various operations on the stack frame.
Here we mainly focus on the operand stack and local variable table in the stack frame.
Instruction execution analysis
Below we analyze in detail the execution process of the above 6 instructions.
Java code
Integer num1 = 1000;
int num2 = num1;
Corresponding bytecode
0: sipush 1000
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: aload_1
8: invokevirtual #3// Method java/lang/Integer.intValue:()I
11: istore_2
The first sentence: sipush 1000
Sipush is a push operation, which means to put the short type number into the stack frame. The stack frame after executing this sentence is as follows:
You can see that the number 1000 is placed in the operand stack.
Second sentence: invokestatic #2
This sentence is to call a static method.
So which method is specifically called?
You can see that there is a #2 parameter behind. This #2 can be understood as an index of the constant pool.
In addition to outputting the bytecode corresponding to the above code, we used the javap -v command. Another important part is the constant pool.
The constant pool of this decompilation is shown in the following figure:
You can see the location of index #2, and it stores a method descriptor. In fact, you can directly see the following comments. This method is the Integer.valueOf method.
I won’t say more about the constant pool here, as everyone knows it for the time being.
Back to our topic. We now know that the instruction invokestatic #2 calls the Integer.valueOf method, so who is the input parameter?
Remember what the last instruction did?
Correct. Put the number 1000 on the top of the operand stack. The number 1000 on the top of the stack is actually the input parameter of Integer.valueOf.
The return value of Integer.valueOf is an Integer object. After executing this instruction, 1000 is popped from the stack, and the result Integer object is pushed onto the stack. This Integer object is num1 in our Java code.
At this time, the stack frame is as follows: The
third sentence: astore_1
This sentence means popping the top element of the operand stack and placing it in the position where the subscript of the local variable table is 1.
The results after execution are as follows:
When this sentence is executed, Integer num1 = 1000 in the Java code, even if the execution is finished.
Boxing conclusion
The first three bytecodes complete the boxing operation. Through the above analysis, we know that the boxing of integers is achieved by calling the Integer.valueOf method.
It is similar for other data types.
The last three sentences of bytecode correspond to the process of int num2 = num1.
The fourth sentence: aload_1
means to push the element whose position is 1 in the local variable table into the operand stack.
The result of this sentence is as follows.
The fifth sentence: invokevirtual #3
Similar to the second sentence of invokestatic, this sentence is also a method call execution.
It's just an example method called here.
Participate in the constant pool situation given in the second sentence analysis, the method called here is the intValue method of Integer.
The operand is the Integer object pushed onto the stack in the previous step.
The results after execution are as follows:
Sixth sentence: istore_2
Store the elements of the operand stack in the second position of the local variable table.
At this point, the execution is complete int num2 = num1
Finally, num1 and num2 are stored in the 1 and 2 positions of the local variable table.
Unboxing conclusion
Through the analysis of the last three bytecodes, we know that the unboxing of the Integer type is implemented using Integer.intValue.
The same is true for other wrapper types.
3. Summary of this article
This article introduces the meaning of boxing and unboxing. Then obtained the bytecode implementation of the assembling and unboxing through the javap command. And analyze these byte codes sentence by sentence.
Finally, for the integer type, the boxing is to use the Integer.valueOf method, and the unboxing is to use the Integer.intValue method to achieve the conclusion.
If this article is helpful to you, please pay attention to my original WeChat public account "Java Technology Station" to receive more of my articles as soon as possible