Auto-unboxing and auto-boxing in Java

This article mainly introduces the knowledge about automatic unboxing and automatic boxing in Java.

basic data type

Basic types, or built-in types, are special types in Java that are different from classes (Class). They are the most frequently used types in our programming.

Java is a strongly typed language. The first declaration of a variable must indicate the data type, and the first variable assignment is called variable initialization.

There are eight basic types in Java, and the basic types can be divided into three categories:

character typechar

Boolean typeboolean

Numeric type byte, short, int, long, float, double.

Numerical types can be divided into integer types byte, short, int, longand floating point types float, double.

There are no unsigned numeric types in Java, and their range of values ​​is fixed and will not change with changes in the machine hardware environment or operating system.

In fact, there is another basic type in Java void, which also has a corresponding wrapper class java.lang.Void, but we cannot directly operate on them.

What are the benefits of primitive data types

We all know that in the Java language, newan object is stored in the heap, and we use these objects through references in the stack; therefore, the object itself consumes more resources.

For frequently used types, such as int, etc., if we need to create a new Java object every time we use this variable, it will be cumbersome. Therefore, like C++, Java provides basic data types. The variables of this data do not need to be created with new. They will not be created on the heap, but stored directly in the stack memory, so it will be more efficient.

Integer value range

Integer types in Java mainly include four types of byte, , shortand int, and longthe range of numbers represented is also from small to large. The reason why the range of representation is different is mainly related to the number of bytes they occupy when storing data.

Let’s start with a short-answer popular science, 1 byte = 8 bits (bit). Integers in Java are signed numbers.

Let's first look at the numbers that 8 bits can represent in calculations:

最小值:10000000 (-128)(-2^7)
最大值:01111111(127)(2^7-1)

Among these types of integers,

  • byte: byte is stored in 1 byte, ranging from -128(-2^7) to 127(2^7-1). When the variable is initialized, the default value of the byte type is 0.

  • short: short is stored in 2 bytes, ranging from -32,768(-2^15) to 32,767(2^15-1). When the variable is initialized, the default value of the short type is 0. Generally, because Java The reason for the transformation itself can be directly written as 0.

  • int: int is stored in 4 bytes, ranging from -2,147,483,648(-2^31) to 2,147,483,647(2^31-1). When the variable is initialized, the default value of the int type is 0.

  • long: long is stored in 8 bytes, ranging from -9,223,372,036,854,775,808 (-2^63) to 9,223,372,036, 854,775,807 (2^63-1). When the variable is initialized, the default value of the long type is 0L or 0l. Write 0 directly.

What to do if it is out of range

As mentioned above, in the integer type, each type has a certain representation range, but some calculations in the program will cause the representation range to exceed, that is, overflow. Such as the following code:

    int i = Integer.MAX_VALUE;
    int j = Integer.MAX_VALUE;

    int k = i + j;
    System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");

Output result: i (2147483647) + j (2147483647) = k (-2)

This is an overflow, and no exception will be thrown when the overflow occurs, and there will be no prompt. Therefore, in the program, when using the same type of data for calculation, we must pay attention to the problem of data overflow.

type of packaging

The Java language is an object-oriented language, but the basic data types in Java are not object-oriented, which has a lot of inconvenience in actual use. In order to solve this problem, when designing classes, design A corresponding class is represented, so the eight classes corresponding to the basic data types are collectively called the wrapper class (Wrapper Class).

The packaging classes are located in java.langthe package, and the corresponding relationship between packaging classes and basic data types is shown in the following table

basic data type Packaging
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double

Among the eight class names, except for Integer and Character classes, the class names of the other six classes are consistent with the basic data types, except that the first letter of the class name is capitalized.

Why do you need wrappers

Many people have doubts, since Java provides eight basic data types in order to improve efficiency, why do we need to provide wrapper classes?

This question, in fact, has already been answered before, because Java is an object-oriented language, and many places need to use objects instead of basic data types. For example, in the collection class, we cannot put int, double and other types into it. Because the container of the collection requires the elements to be of type Object.

In order to make the basic type also have the characteristics of an object, a wrapper type appears, which is equivalent to "wrapping" the basic type, making it have the nature of an object, and adding properties and methods to it, enriching the operations of the basic type .

Unboxing and packing

Well, with the basic data types and wrapper classes, there must be conversions between them sometimes. For example, convert an int of a basic data type into an Integer object of a wrapper type.

We think that the packaging class is the packaging of the basic type, so the process of converting the basic data type into the packaging class is packaging. English corresponds to boxing, and Chinese translates to boxing.

Conversely, the process of converting a packaging class into a basic data type is unpacking. English corresponds to unboxing, and Chinese translates to unboxing.

Before Java SE5, to do boxing, you can pass the following code:

    Integer i = new Integer(10);

Auto-unboxing and auto-boxing

In Java SE5, in order to reduce the developer's work, Java provides automatic unboxing and automatic boxing functions.

Autoboxing: It is to automatically convert basic data types into corresponding packaging classes.

Automatic unboxing: It is to automatically convert the packaging class into the corresponding basic data type.

    Integer i = 10;  //自动装箱
    int b = i;     //自动拆箱

Integer i=10It can be replaced Integer i = new Integer(10);, because Java provides us with the function of autoboxing, which does not require developers to manually create a new Integer object.

The realization principle of automatic boxing and automatic unboxing

Now that Java provides the ability to automatically unbox, let's take a look at what the principle is and how Java implements the automatic unboxing function.

We have the following code for automatic unboxing:

    public static  void main(String[]args){
    
    
        Integer integer=1; //装箱
        int i=integer; //拆箱
    }

After decompiling the above code, the following code can be obtained:

    public static  void main(String[]args){
    
    
        Integer integer=Integer.valueOf(1);
        int i=integer.intValue();
    }

From the decompiled code above, it can be seen that the automatic boxing of int is Integer.valueOf()realized through the method, and the automatic unboxing of Integer is integer.intValuerealized through the method. If readers are interested, you can try to decompile all eight types, and you will find the following rules:

Automatic boxing is valueOf()achieved through the method of the packaging class. Automatic unboxing is xxxValue()achieved through the method of the packaging class object.

Which places will automatically unpack

After we understand the principle, let's take a look at the circumstances under which Java will automatically unbox for us. The scenario of variable initialization and assignment mentioned above will not be introduced, it is the simplest and easiest to understand.

Let's mainly look at the scenarios that may have been overlooked.

Scenario 1. Put basic data types into collection classes

We know that collection classes in Java can only receive object types, so why doesn't the following code report an error?

    List<Integer> li = new ArrayList<>();
    for (int i = 1; i < 50; i ++){
    
    
        li.add(i);
    }

Decompile the above code to get the following code:

    List<Integer> li = new ArrayList<>();
    for (int i = 1; i < 50; i += 2){
    
    
        li.add(Integer.valueOf(i));
    }

From the above, we can conclude that when we put the basic data type into the collection class, it will be automatically boxed.

Scenario 2. Size comparison between packaging type and basic type

Has anyone ever wondered what is actually being compared when we compare the size of an Integer object to a primitive type? Look at the following code:

    Integer a = 1;
    System.out.println(a == 1 ? "等于" : "不等于");
    Boolean bool = false;
    System.out.println(bool ? "真" : "假");

Decompile the above code to get the following code:

    Integer a = 1;
    System.out.println(a.intValue() == 1 ? "等于" : "不等于");
    Boolean bool = false;
    System.out.println(bool.booleanValue ? "真" : "假");

It can be seen that the comparison operation between the wrapper class and the basic data type is to first unbox the wrapper class into a basic data type, and then compare.

Scenario 3. Operation of packaging type

Has anyone ever wondered how it works when we perform four arithmetic operations on Integer objects? Look at the following code:

    Integer i = 10;
    Integer j = 20;

    System.out.println(i+j);

The decompiled code is as follows:

    Integer i = Integer.valueOf(10);
    Integer j = Integer.valueOf(20);
    System.out.println(i.intValue() + j.intValue());

We found that operations between two packaging types will be automatically unboxed into basic types.

Scenario 4, the use of ternary operators

This is a scene that many people don't know, and the author only learned about it after an online bloody bug happened. Look at the code of a simple ternary operator:

    boolean flag = true;
    Integer i = 0;
    int j = 1;
    int k = flag ? i : j;

Many people don't know that in int k = flag ? i : j;this line, automatic unboxing will occur (before JDK1.8, see for details: "Alibaba Java Development Manual - Taishan Edition" What is the null pointer problem of the ternary operator mentioned? ? ).

The decompiled code is as follows:

    boolean flag = true;
    Integer i = Integer.valueOf(0);
    int j = 1;
    int k = flag ? i.intValue() : j;
    System.out.println(k);

This is actually the grammatical specification of the ternary operator. When the second and third operands are primitive types and objects respectively, the objects will be unboxed to primitive types for operation.

Because in the example, flag ? i : j;in the fragment, i in the second paragraph is an object of packaging type, and j in the third paragraph is a basic type, so the packaging class will be automatically unboxed. If the value of i is this time null, NPE will occur. ([Automatic unboxing causes null pointer exception][1])

Scenario 5. Function parameters and return values

This is easier to understand, directly on the code:

    //自动拆箱
    public int getNum1(Integer num) {
    
    
     return num;
    }
    //自动装箱
    public Integer getNum2(int num) {
    
    
     return num;
    }

Automatic unboxing and caching

Java SE's automatic unboxing also provides a function related to caching. Let's first look at the following code and guess the output:

    public static void main(String... strings) {
    
    

        Integer integer1 = 3;
        Integer integer2 = 3;

        if (integer1 == integer2)
            System.out.println("integer1 == integer2");
        else
            System.out.println("integer1 != integer2");

        Integer integer3 = 300;
        Integer integer4 = 300;

        if (integer3 == integer4)
            System.out.println("integer3 == integer4");
        else
            System.out.println("integer3 != integer4");
    }

We generally believe that the results of the above two judgments are false. Although the compared values ​​are equal, but because the objects are compared, and the references of the objects are different, the two if judgments are considered to be false. In Java, ==object references are compared, whereas equalsvalues ​​are compared. So, in this example, different objects have different references, so both will return false when doing the comparison. Strangely, here two similar if conditionals return different boolean values.

The real output of the above code is:

integer1 == integer2
integer3 != integer4

The reason is related to the caching mechanism in Integer. In Java 5, a new feature was introduced on operations on Integer to save memory and improve performance. Integer objects enable caching and reuse by using the same object reference.

Works with integer values ​​in the range -128 to +127.

Applies only to autoboxing. Creating an object using a constructor does not apply.

For the specific code implementation, you can read the article [Integer Cache Mechanism in Java][2], which will not be elaborated here.

We just need to know that when autoboxing is required, if the number is between -128 and 127, the object in the cache will be used directly instead of recreating an object.

The Javadoc in it explains in detail the autoboxing process between cache support -128 and 127. The maximum value of 127 can be -XX:AutoBoxCacheMax=sizemodified by .

In fact when this feature was introduced in Java 5, the range was fixed -128 to +127. Later in Java 6, java.lang.Integer.IntegerCache.highthe maximum value can be set via .

This allows us to flexibly adjust to improve performance according to the actual situation of the application. What was the reason for choosing this -128 to 127 range? Because this range of numbers is the most widely used. In the program, when using Integer for the first time, it also takes some extra time to initialize the cache.

The Java Language Specification (JLS) in the Boxing Conversion section states the following:

If the value of a variable p is:

  • Integer between -128 and 127 (§3.10.1)
  • Boolean values ​​of true and false (§3.10.3)
  • \u0000\u007fcharacters between to (§3.10.4)

When within the range, when p is wrapped into two objects a and b, you can directly use a == b to judge whether the values ​​of a and b are equal.

Problems caused by automatic unboxing

Of course, automatic unboxing is a very good function, which greatly saves the developer's energy, and no longer needs to care about when the box needs to be unpacked. However, he also introduces some problems.

The value comparison of the package object cannot be used simply ==. Although the number between -128 and 127 is OK, the comparison is still required outside this range equals.

As mentioned earlier, some scenarios will perform automatic unboxing. At the same time, I also said that due to automatic unboxing, if the packaging object is null, NPE may be thrown during automatic unboxing.

If there are a lot of unboxing operations in a for loop, a lot of resources will be wasted.

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132307188