JavaRush /Java Blog /Random EN /Autoboxing and unboxing in Java
Viacheslav
Level 3

Autoboxing and unboxing in Java

Published in the Random EN group
<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.
Autoboxing and unboxing in Java - 1
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:

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 Stringthe parameters, we get Integerit 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.classwhere App.class is the compiled class file for your class. We will see content like this:
Autoboxing and unboxing in Java - 2
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:
Autoboxing and unboxing in Java - 3
That is, in essence, a new one will be taken Integeror will be obtained Integerfrom the cache (the cache is nothing more than just an array of Integers) depending on the value of the number. Naturally, Integernot 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.asListwill not make from int[]for us Listfrom 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.absaccepts 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:
Autoboxing and unboxing in Java - 4
As you can see, no magic. Everything is within Java. It just works “by itself”. For our convenience. <h2>Rake</h2>
Autoboxing and unboxing in Java - 5
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 ==. 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 Integervalue 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.intValueand crash, because... valuewill 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 printlntake an object as input and need to somehow connect the lines. Lines... yes, that's why there is no packaging as such. There Integerare 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
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION