JavaRush /Java Blog /Random EN /Autopacking and unpacking in Java
Viacheslav
Level 3

Autopacking and unpacking in Java

Published in the Random EN group
<h2>Introduction</h2>The 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, the language should conveniently express our thoughts.
Autopacking and unpacking in Java - 1
So, Java SE 5 introduced the boxing/unboxing mechanism. And a separate tutorial from Oracle is devoted to the features of this means of expressing thoughts: Autoboxing and Unboxing . <h2>Autoboxing Boxing</h2>Let's look at an example of autoboxing 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 value of the port. As we can see, since we read the port value from Stringthe parameters, we get Integerit by getting it via 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 look "under the hood", as they say. Download the source code from compilejava.net by clicking "Download ZIP". After that, extract the downloaded archive to 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:
Autopacking and unpacking in Java - 2
This is the same notorious "bytecode". But what matters to us now is what we see. First, primitive 8080 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:
Autopacking and unpacking in Java - 3
That is, in fact, a new one will be taken Integeror will be obtained Integerfrom the cache (the cache is nothing more than just an array of Integer) depending on the magnitude of the value of the number. Naturally, Integernot one is 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 at the very bottom of Oracle's Tutorial: " Autoboxing and Unboxing ". It should be noted right away that arrays of primitives do not have a "wrapper" without connecting any third-party libraries. Those. Arrays.asListwon't make out int[]for us Listout Integer's. <h2>Unboxing</h2>The reverse process to boxing is called unboxing unboxing. Consider 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, for Integerthis method is intValue . If we look at the bytecode, then it is:
Autopacking and unpacking in Java - 4
Apparently, no magic. All within Java. It just works on its own. For our convenience. <h2>Rake</h2>
Autopacking and unpacking in Java - 5
Any tool, if misused, becomes a formidable weapon against itself. And the mechanism of autoboxing and unboxing boxing/unboxing 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 Boxing's explanation above), while 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 upper cache bound ( java.lang.Integer.IntegerCache.high ). In addition, this border may change due to other settings. On this subject, see the discussion 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 values ​​from the cache (ie -128 to 127), then a new object will be created each time. If suddenly boxing (i.e. boxing) is performed in a cycle, this will cause a huge increase in unnecessary objects and resource consumption for the work of the garbage collector. Therefore, do not be too reckless about it. The third no less sick rake follows 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 the input comes null, then instead of an understandable error, we will get an indistinct one NullPointerException. Because for comparison, Java will try to execute value.intValueand fall down, because valuewill be null. <h2>Output</h2>The boxing/unboxing mechanism allows the programmer to write less code and sometimes not even think about converting from primitives to objects and vice versa. But that doesn't mean you have to forget how it works. Otherwise, you can make a mistake that may not pop up right away. Do not rely on parts of the system that are not entirely under our control (such as the integer boundary). But do not forget about all the advantages of wrapper classes (likeInteger). 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 follow-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'));
}
It is also the correct conclusion from everything - there is no magic, there is some kind of implementation. And not everywhere 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 into one line by the compiler. That is, as if you wrote "The number is 8". And in the example below, there will also be no packaging:
public static void main(String[] args) {
    System.out.println("The number is " + Math.abs(-2));
}
How is it when we printlnhave an object as input and we need to somehow connect the lines. Strings... yes, that's why there is no packaging as such. It Integerhas static methods, but some of them are package. That is, we cannot use them, but in Java itself they can be actively used. Here is just such a case. The getChars method will be called, which makes an array of characters from a number. Again, no magic, just Java). So in any incomprehensible situation, you should just 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