This article mainly introduces the Java automatic unpacking and automatic packing knowledge.

Basic data types

Primitive types, or built-in types, are special types in Java that differ from classes. They are the most frequently used types in our programming.

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

There are eight Java primitive types, which can be divided into three categories:

Character type char

Boolean type Boolean

The numeric types byte, short, int, long, float, double.

Numeric types can be divided into integer types byte, short, int, and long, and floating point types float and double.

There are no unsigned numeric types in Java; their value ranges are fixed and do not change with the machine’s hardware environment or operating system.

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

What are the benefits of primitive data types

We all know that in the Java language, new objects are stored in the heap, and we use those objects by reference in the stack; So, objects themselves are resource-intensive.

For frequently used types such as int, it would be cumbersome if we needed to new a Java object every time we used such a variable. So, like C++, Java provides basic data types whose variables do not need to be created using new. They are not created on the heap, but stored directly in stack memory, so it is more efficient.

The value range of an integer

In Java, integers mainly include byte, short, int, and long, and the range of numbers they represent varies from small to large. The difference in the range is mainly related to the number of bytes they occupy when storing data.

1 byte =8 bits. Integers in Java are signed numbers.

Let’s take a look at the number represented by 8bit in the calculation:

Minimum value: 10000000 (-128) (-2^7) Maximum value: 01111111 (127) (2^7-1)Copy the code

Of these types of integers,

  • Byte: Byte is stored in one byte. The value ranges from -128(-2^7) to 127(2^7-1). The default value of byte type is 0 when the variable is initialized.

  • Short: Short is stored in 2 bytes and ranges from -32,768 (-2^15) to 32,767 (2^15-1). When the variable is initialized, the default value of type short is 0. Normally, due to Java transformations, it can be written to 0.

  • Int: Int is stored in four bytes, ranging from -2,147,483,648 (-2^31) to 2,147,483,647 (2^31-1). When the variable is initialized, int defaults to 0.

  • Long: With 8 bytes long storage, the range of (2 ^ 63) to 9223372036854775808-9223372036, 854775807 (2 ^ 63-1), when the variable is initialized, the default value is 0 l long or 0 l, Or you could just write it as 0.

What to do out of range

As mentioned above, each type of an integer has a range of representations. However, there are certain calculations in the program that result in an 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 + ")");
Copy the code

Output: I (2147483647) + j (2147483647) = k (-2)

** This is an overflow, and an overflow does not throw an exception, and there is no hint. ** Therefore, in the program, when using the same type of data to perform operations, must pay attention to the problem of data overflow.

Packing type

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 practical use. In order to solve this deficiency, a corresponding class is designed to represent each basic data type in the design of classes. The eight classes that correspond to the basic data types are called Wrapper classes.

The wrapper classes are all in the java.lang package, and the mapping between wrapper classes and basic data types is shown in the following table

Basic data types A wrapper class
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double

Except for Integer and Character, the other six classes have the same class name as the basic data type, except that the first letter of the class name is capitalized.

Why wrapper classes

Many people wonder why you should provide a wrapper class when Java provides eight basic data types for efficiency.

This question is already answered because Java is an object-oriented language and there are many places where you need to use objects rather than primitive data types. For example, in a collection class, we can’t put ints, doubles, etc. Because the container of the collection requires the element to be of type Object.

In order to make the base type behave like an object, wrapper types are developed, which “wrap” the base type to behave like an object, add properties and methods to it, and enrich the operations of the base type.

Unpacking and packing

So, with primitive data types and wrapped classes, there must be times when you have to convert between them. For example, an int of a primitive data type is converted to an Integer object of a wrapper type.

We consider the packing class to be the packing of the basic type. Therefore, the process of converting the basic data type to the packing class is boxing, which corresponds to The English version of Boxing.

Instead, the process of converting a wrapper class to a basic datatype is unboxing.

Prior to Java SE5, boxing was done with the following code:

Integer i = new Integer(10);
Copy the code

Automatic unpacking and automatic packing

In Java SE5, to reduce developer work, Java provides auto-unboxing and auto-boxing.

Automatic boxing: The automatic conversion of the base data type to the corresponding wrapper class.

Automatic unpacking: The automatic conversion of the packaging class to the corresponding basic data type.

Integer i =10; Int b= I; // Automatic unpackingCopy the code

Integer I =10 can replace Integer I = new Integer(10); This is because Java provides automatic boxing without requiring the developer to manually new an Integer object.

The realization principle of automatic packing and automatic unpacking

Since Java provides automatic unboxing, let’s take a look at how it works and how Java implements automatic unboxing.

We have the following code for automatic unpacking:

public static void main(String[]args){ Integer integer=1; Int I =integer; / / devanning}Copy the code

Decompiling the code above yields the following code:

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

As you can see from the decomcompiled code above, the automatic boxing of int is implemented by integer.valueof () method, and the automatic unboxing of Integer is implemented by integer.intValue. If you’re interested, try decompiling all eight types and you’ll notice the following:

Automatic boxing is all done through the valueOf() method of the wrapper class. Automatic unpacking is done by wrapping the xxxValue() of the class object.

Where will be automatically unpacked

Now that we’ve seen how this works, let’s take a look at the situations in which Java will help us with auto-unboxing. The previously mentioned variable initialization and assignment scenarios will not be covered; they are the simplest and easiest to understand.

Let’s focus on scenarios that might have been overlooked.

Scenario 1: Put the base data type into the collection class

We know that collection classes in Java can only accept 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);
}
Copy the code

Decompiling the code above yields the following code:

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

From the above, we can conclude that when we put basic data types into collection classes, they are automatically boxed.

Scenario 2. Size comparison of the wrapper type and base type

Has anyone ever wondered what we’re actually comparing an Integer object to a primitive type? Look at the following code:

Integer a=1; System.out.println(a==1?" Equal to ":" not equal to "); Boolean bool=false; System.out.println(bool?" True ":" false ");Copy the code

Decompiling the above code yields the following code:

Integer a=1; System.out.println(a.int value ()==1?); Boolean bool=false; System.out.println(bool.booleanValue?" True ":" false ");Copy the code

As you can see, the comparison between the wrapper class and the basic data type is performed by unboxing the wrapper class into the basic data type and then comparing it.

Scenario 3. Operation of the package type

Has anyone ever wondered what happens when we do four operations on an Integer object? Look at the following code:

    Integer i = 10;
    Integer j = 20;

    System.out.println(i+j);
Copy the code

The decompiled code is as follows:

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

We found that the operation between the two packaging types is automatically unpacked into the basic type.

Use of scenario 4 and ternary operators

This is a scene that many people don’t know, and the author learned about it after a bloody Bug happened online. Look at the code for a simple ternary operator:

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

A lot of people don’t know, but int k = flag, right? i : j; In this row, automatic unpacking occurs. 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);
Copy the code

This is actually the syntax specification for the ternary operator. When the second and third operands are primitives and objects respectively, the objects are unboxed as primitives.

Because in the example, flag? i : j; In the fragment, I in the second segment is a wrapper type object, and j in the third segment is a primitive type, so the wrapper class is automatically unpacked. If the value of I is null, then an NPE will occur. (Abnormal null pointer due to automatic unpacking)

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; }Copy the code

Automatic unboxing and caching

Java SE autounboxing also provides a caches related feature. Let’s take a look at the following code to 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"); }Copy the code

We generally believe that both of the above judgments are false. Although the values are equal, both if judgments are considered false because we are comparing objects and their references are different. In Java, == compares object applications, and equals compares values. So, in this example, different objects have different references, so all comparisons will return false. Strangely, two similar if conditional judgments here return different Booleans.

The actual output from this code is:

integer1 == integer2 integer3 ! = integer4Copy the code

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

Applies to integer values from -128 to +127.

Only applicable to automatic packing. Using constructors to create objects is not appropriate.

The code implementation can be found in the Java article Caching for integers, which is not covered here.

All we need to know is that when automatic boxing is required, if the number is between -128 and 127, the object in the cache will be used directly instead of creating a new object.

The Javadoc details that the cache supports automatic boxing between -128 and 127. The maximum value 127 can be changed by running -xx :AutoBoxCacheMax=size.

In fact, when this feature was introduced in Java 5, it had a fixed range of -128 to +127. Later in the Java 6, can through the Java lang. Integer. IntegerCache. High set maximum.

This gives us the flexibility to adjust to the actual situation of the application to improve performance. What was the reason for choosing this — 128 to 127? Because this range of numbers is the most widely used. The first time Integer is used in a program, it also takes some extra time to initialize the cache.

The Java language specification (JLS) for the Boxing Conversion section states as follows:

If the value of a variable p is:

- Integers between 128 and 127 (§3.10.1) Boolean values of true and false (§3.10.3) Characters between '\u0000' and '\u007f' (§3.10.4)Copy the code

When p is packaged into two objects a and B, a==b can be directly used to determine whether the values of a and B are equal.

Problems with automatic unpacking

Of course, auto-unboxing is a nice feature that saves developers a lot of effort worrying about when they need to unpack. But he also introduces some problems.

Wrapping numeric comparisons of objects cannot be done simply with ==, although numbers between -128 and 127 can be used, but outside of that range equals is still required.

As mentioned earlier, there are some scenarios for automatic unpacking, and we also said that because of automatic unpacking, if the wrapper object is null, then it is possible to throw an NPE during automatic unpacking.

If you have a lot of unboxing in a for loop, you can waste a lot of resources.

The resources

Automatic unboxing of Java