<h2>Introduction</h2>A programming language, like the language people speak, lives and changes, new phenomena appear in it to make the language more convenient to use. And as we know, language should conveniently express our thoughts.
So, in Java SE 5, the boxing/unboxing mechanism was introduced. And a separate tutorial from Oracle is devoted to the features of this means of expressing thoughts: Autoboxing and Unboxing . <h2>Auto-packing Boxing</h2>Let's look at an example of auto-packing Boxing. First, let's see how it works. Let's use the site compilejava.net and create a class:
This is the same notorious “bytecode”. But what is important to us now is what we see. First, the 8080 primitive is placed on the method execution stack, and then Integer.valueOf is executed . This is the “magic” of boxing. And inside the magic looks like this:
That is, in essence, a new one will be taken
As you can see, no magic. Everything is within Java. It just works “by itself”. For our convenience. <h2>Rake</h2>
Any tool, if used incorrectly, becomes a formidable weapon against itself. And the automatic boxing/unboxing mechanism in Java is no exception. The first, obvious comparison is through
public class App {
public static void main(String[] args) {
Integer portNumber = 8080;
if (args.length != 0) {
portNumber = Integer.valueOf(args[0]);
}
System.out.println("Port number is: " + portNumber);
}
}
Simple code. We can specify the input parameter and change the port value. As we see, because we read the port value from String
the parameters, we get Integer
it by getting it through Integer.valueOf
. Therefore, we are forced to specify it not as a primitive type, but as an object type Integer
. And here we get on the one hand, we have an object variable, and the default value is a primitive. And it works. But we don't believe in magic, do we? Let's take a look “under the hood,” as they say. Download the source code from compilejava.net by clicking “Download ZIP”. After that, extract the downloaded archive into a directory and go to it. Now let's do: javap -c -p App.class
where App.class is the compiled class file for your class. We will see content like this:
Integer
or will be obtained Integer
from the cache (the cache is nothing more than just an array of Integers) depending on the value of the number. Naturally, Integer
not only one was so lucky. There is a whole list of related primitive types and their wrappers (classes that represent primitives in the OOP world). This list is given at the very bottom of the Tutorial from Oracle: “ Autoboxing and Unboxing ”. It’s worth noting right away that arrays made from primitives do not have a “wrapper” without connecting any third-party libraries. Those. Arrays.asList
will not make from int[]
for us List
from Integer
's. <h2>Unboxing</h2>The reverse process to boxing is called unboxing unboxing. Let's look at an unpacking example:
public class App {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Please, enter params");
return;
}
int value = Math.abs(Integer.valueOf(args[0]));
System.out.println("Absolute value is: " + value);
}
}
Math.abs
accepts only primitives. What to do? The wrapper class has a special method for this case that returns a primitive. For example, this is the intValueInteger
method . If we look at the bytecode, it is like this:
==
. I think this is clear, but let's look at it again:
public static void main(String[] args) {
Integer inCacheValue = 127;
Integer inCacheValue2 = 127;
Integer notInCache = 128; // new Integer(129)
Integer notInCache2 = 128; // new Integer(129)
System.out.println(inCacheValue == inCacheValue2); //true
System.out.println(notInCache == notInCache2); //false
}
In the first case, the value is taken from the Integer
value cache (see explanation of Boxing above), and in the second case a new object will be created each time. But here it is worth making a reservation. This behavior depends on the cache high bound ( java.lang.Integer.IntegerCache.high ). In addition, this limit may change due to other settings. You can read the discussion on this topic on stackoverflow: How large is the Integer cache? Naturally, objects need to be compared using equals: System.out.println(notInCache.equals(notInCache2));
The second problem associated with the same mechanism is performance. Any boxing in Java is equivalent to creating a new object. If the number is not included in the cache values (ie -128 to 127), then a new object will be created each time. If suddenly packaging (i.e. boxing) is performed in a loop, this will cause a huge increase in unnecessary objects and consumption of resources for the work of the garbage collector. Therefore, don't be too reckless about it. A third, no less painful rake stems from the same mechanism:
public static void check(Integer value) {
if (value <= 0) {
throw new IllegalStateException("Value is too small");
}
}
In this code, the person was clearly trying not to get past the error. But there is no check for null
. If it comes to the input null
, then instead of an understandable error we will get an incomprehensible one NullPointerException
. Because for comparison, Java will try to execute value.intValue
and crash, because... value
will null
. <h2>Conclusion</h2>The boxing/unboxing mechanism allows the programmer to write less code and sometimes not even think about converting from primitives to objects and back. But that doesn't mean you should forget how it works. Otherwise, you may make a mistake that may not appear immediately. We should not rely on parts of the system that are not completely under our control (such as the integer boundary). But don’t forget about all the advantages of wrapper classes (like Integer
). Often these wrapper classes have a set of additional static methods that will make your life better and your code more expressive. Here's a catch-up example:
public static void main(String[] args) {
int first = 1;
int second = 5;
System.out.println(Integer.max(first, second));
System.out.println(Character.toLowerCase('S'));
}
The correct conclusion from everything is that there is no magic, there is some kind of realization. And not everything will always be what we expect. For example, there is no packaging: System.out.println("The number is " + 8);
The example above will be optimized by the compiler into one line. That is, it’s as if you wrote “The number is 8”. And in the example below there will be no packaging either:
public static void main(String[] args) {
System.out.println("The number is " + Math.abs(-2));
}
How can it be when we println
take an object as input and need to somehow connect the lines. Lines... yes, that's why there is no packaging as such. There Integer
are static methods, but some of them are package
. That is, we cannot use them, but in Java itself they can be actively used. This is exactly the case here. The getChars method will be called, which makes an array of characters from the number. Again, no magic, just Java). So in any unclear situation, you just have to look at the implementation and at least something will fall into place. #Viacheslav
GO TO FULL VERSION