Author: Wheat7, Reviewed by: ShixinZhang, Complete date: 2018.1.25

After reading this article, you will know:

background

Hello, this is the third article in the Java Basics series, part one of the Android Advanced Skill Tree Project.

We do this activity, in addition to ensure the comprehensive knowledge point, complete, but also want to let each article has its own thinking, as far as possible to combine knowledge point with practice, and strive to let readers read something. Every friend has a job, and every knowledge point needs to go through many processes, such as thinking, learning, writing, submission, review, modification, editing, publishing, etc., so the overall time will be slower. I apologize to you first.

The “Java Basics Series” has about 12 chapters, which are mainly as follows:

  1. Abstract Classes and interfaces (done)

  2. Inner class (done)

  3. The modifier

  4. The split open a case using the

  5. annotations

  6. reflection

  7. The generic

  8. Exception (done)

  9. A collection of

  10. IO

  11. string

  12. other

Today, let’s talk about automatic unboxing in Java. Welcome to discuss more.

What is automatic unboxing

Auto-unboxing was introduced in Java5(Java1.5, renamed later), which is, to put it bluntly, syntactic sugar.

Automatic boxing is when the compiler automatically converts the underlying type value into the corresponding wrapper object, such as converting an int variable into an Integer object. This process is called boxing. Converting an Integer object to a value of type int is called unboxing.

Base and reference types

If you have a little Java background, you should know the Java data types, which are classified into basic types and reference types.

The basic types can be divided into four types and eight types, which are commonly known as:

  • Four types of integers:

    • Byte, short, int, long

    • They differ in the amount of data they can store, which means they allocate different amounts of memory

  • Two floating point types

    • 32-bit single-precision floating-point float, 64-bit double

  • A Unicode encoded character unit char

  • And finally, Boolean, true Boolean type

An important point related to today’s topic is that a member of a class’s underlying type is stored in stack memory and initialized when the object is created. Whether you use it or not, it is given a default initial value, such as int, which is 0.

And let’s expand on this:

int a = 1
int b = 1
System.out.printf(a == b) ---- trueCopy the code

Values of the same basic type refer to the same memory area, while == determines the memory address, i.e. whether they are the same object, so return true.

Note: The equality of reference types is not determined by ==, but by equals().

Reference types are classified into classes, interfaces, and arrays. It is called “reference type” because the object of our reference type exists in heap memory, and what we hold is a reference in stack memory to the corresponding heap memory.

What does this have to do with automatic unpacking? See below.

Holds object & wrapper classes

In some cases, we may want to hold many objects, using our common collection classes, which are designed to hold the single root superclass Object for all of our types by default.

When an Object is loaded into a collection, it is cast up to Object, and when it is taken out, it is cast back to the original type that we loaded by parameterizing the type using the generic diamond

syntax.

What if we want to hold the basic type?

The base types have no superclasses, and the operations above are not appropriate for them. The JDK then wraps a wrapper class for each base type:

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

So we can do this

List<Interger> intList = new ArrayList<>(); intList.add(1); // Automatic boxing is performed hereCopy the code

Note that this is what we mean by auto-boxing, because add() needs to pass in the parameterized type that the List holds, which is the wrapper type of int Integer, Instead, we pass in a value of type int, and that int is automatically wrapped by the compiler as an Integer, which is auto-boxing.

As you might expect, arrays can be used to hold primitive types. But sometimes the number of objects we want to hold is uncertain, and arrays have to be initialized to a certain length, which makes arrays very limited to hold primitive types, or objects.

Collection holding objects are application points for wrapping classes.

Wrapping classes gives the base type the properties of an object and adds properties and methods to enrich the operations of the base type.

Automatic unpacking

For automatic unboxing, see the code.

Before Java5, to get an Integer value, write:

// Box Integer Integer = new Integer(10); Int I = integer.intValue();Copy the code

After Java5 it is much simpler:

// Automatic boxing Integer Integer = 10; Int I = integer;Copy the code

You can see that before Java5, when you needed an object of type Integer, you needed to new it out just like any other object (calling the static method integer.valueof (3) to create an object that was also new inside); Unboxing requires calling intValue() to fetch an int. After Java5, you can create an object of type Integer and assign it directly to an int, which in turn can be assigned to a variable of type int.

Simply put: Autopack/unpack is the basic type and its packaging class can be directly converted to each other.

There are many scenarios for auto-unboxing other than when the compiler does this automatically at assignment time.

When will automatic unpacking take place

1. The assignment

We have seen above, don’t say.

2. When a method call is passed a parameter

public void argAutoBoxing(Integer i) { } argAutoBoxing(1); // Automatic boxing is performed hereCopy the code
public void argAutoUnBoxing(int i) { } argAutoUnBoxing(new Integer(1)); // There is automatic unpackingCopy the code

3. When operated by an operator

Integer integer = new Integer(1); Int I = interger + 1Copy the code

How is automatic unpacking implemented

ValueOf (int I); valueOf(int I); valueOf(int I); Automatic unpacking automatically calls integer.intValue (), and so on.

Knowing the concept of automatic packing and unpacking, we also need to pay attention to what problems it can cause us?

What problems should be paid attention to in automatic packing and unpacking?

1. Performance problems

Create objects in the heap memory consumption is certainly more than use stack memory, in automatic container loading at the same time, also has some performance overhead, if the data volume is larger, or is under the condition of circulation, frequent container loading and generate a wrapper class, the performance impact is a fragment, so is not a special demand, For example, in the case of collections held above, primitive types are used instead of wrapper classes.

Here’s an example:

Integer sum = 0;
 for(int i=1000; i<5000; i++){
   sum+=i;
}Copy the code

The code sum+= I can be regarded as sum = sum+ I. When sum is operated by the + operator, it will automatically unpack the sum and add the values. Finally, it will automatically pack the sum into an Integer object.

The internal changes are consistent with the following code:

sum = sum.intValue() + i;
Integer sum = new Integer(result);Copy the code

Sum is of type Integer, and 4000 useless Integer objects are created in the above loop, which degrades program performance and increases garbage collection.

So when we program, we need to be aware of this and declare variable types correctly to avoid performance problems caused by automatic boxing.

As another example, the performance of a HashMap in Java is also affected by automatic unboxing.

By default, a HashMap receives a HashMap
parameter. Therefore, a large amount of automatic unpacking of Key values is performed when adding, deleting, modifying, or checking them. To solve this problem, Java provides SparseArray. Including SparseBoolMap, SparseIntMap, SparseLongMap, and LongSparseMap.,>

For example, SparseIntMap is SparseIntMap

, which avoids automatic unpacking and reduces memory consumption.
,>

2. Heavy loading and automatic packing

Before JDK5, value(int I) and value(Integer O) were completely different methods, and developers didn’t get confused about which method to call if it was an int or an Integer.

But with the introduction of automatic boxing and unboxing, handling overloaded methods is a little more complicated, such as remove(int index) and remove(Object O) in ArrayList, If the collection holds three objects of type Integer 3,1,2, and we call remove(3), which overloaded method of remove is called?

Remove the object with value 3, or remove the object with value 2 with index 3?

In other words, the question is, does parameter 3 get packaged automatically?

The answer is: no!

In this case, the compiler doesn’t do auto-unboxing, so it calls remove(int index), and the Integer object with an index of 3 and a value of 2 is removed.

We can verify this through the following examples:

public void testAutoBoxing(int i){
    System.out.println("primitive argument");
 
}
 
public void testAutoBoxing(Integer integer){
    System.out.println("wrapper argument");
 
}
 
//calling overloaded method
int value = 1;
test(value); //no autoboxing 
Integer iValue = value;
test(iValue); //no autoboxing
 
Output:
primitive argument
wrapper argumentCopy the code

3. Cache value problem

This is a common interview question:

public class Main {
    public static void main(String[] args) {
 
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;
 
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

Output:
true
falseCopy the code

Why does this work? Let’s have a look at the source code:

public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }Copy the code

Well, the problem lies in the IntegerCache class. Let’s look at the IntegerCache implementation.

private static class IntegerCache { static final int high; static final Integer cache[]; static { final int low = -128; // high value may be configured by property int h = 127; if (integerCacheHighPropValue ! = null) { // Use Long.decode here to avoid invoking methods that // require Integer's autoboxing cache to be initialized  int i = Long.decode(integerCacheHighPropValue).intValue(); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - -low); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }Copy the code

If the value is between [-128,127], then a reference is returned to an existing object in integerCache. cache. Otherwise, a new Integer object is created.

The values of i1 and i2 are 100, so i1 and i2 refer to the same object, while i3 and i4 are out of the cache value range, so two different objects are created.

After reading the above, can you do another interview question?

Public class Main {public static void Main (String[] args) {Double i1 = 100.0; Double i2 = 100.0; Double i3 = 200.0; Double i2 = 200.0; System.out.println(i1==i2); System.out.println(i3==i4); } } Output: flase flaseCopy the code

As for why, small partners can look at the source code.

The main point here is that each wrapper class has a caching mechanism to reduce resource consumption in general, but the mechanism of each wrapper class is definitely different and needs to be explored. Feel free to leave a comment after exploring to deepen your impression.

4. = = and equals ()

From: https://www.zhihu.com/question/26872848

The equals() and == operations are used to compare objects and check the equality of two objects, but the main difference is that the former is a method and the latter is an operator.

Since Java doesn’t support overloading, the behavior of == is exactly the same as equals() for each object, but equals() can be overridden depending on the business rules.

Another difference to note is that the == convention is used for comparisons between primitive (primitive) types, while equals() is only used for comparisons between objects.

The main difference between == and equals is that == is often used to compare native types, while the equals() method is used to check the equality of objects.

Another difference is that if == and equals() are used to compare objects, == returns true when the two reference addresses are the same; Whether equals() returns true or false depends on the override implementation.

Here we use code to illustrate using == and equals() when wrapping classes and auto-unboxing:

public class Main {
    public static void main(String[] args) {
 
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
 
        System.out.println(c==d);
        System.out.println(e==f);
        System.out.println(c==(a+b));
        System.out.println(c.equals(a+b));
        System.out.println(g==(a+b));
        System.out.println(g.equals(a+b));
        System.out.println(g.equals(a+h));
    }
}

Output:
true
false
true
true
true
false
trueCopy the code

Did you get all of these results right?

In use and automatic container wrapper classes, use the = = operator, if two operands are wrapper types of references, whether is compared to the same object, and if one operand is expression (i.e., contains arithmetic), compare the numerical (top when it comes to the use of the operator of triggered automatically split open a case).

In addition, for wrapper types, equals() does not perform a type conversion, comparing the value of the object, as we usually do with strings.

Now that you understand this, you should be able to make sense of the results. The first and second sentences are due to the caching mechanism mentioned above.

A + B involves an arithmetic operation that triggers the automatic unpacking process, so they compare values for equality.

For c.equals(a+ B), the automatic unpacking process is triggered, and then the automatic packing process is triggered. In other words, a+ B will call intValue (), and then call Integer.valueof, and then compare equals.

5. AlertNullPointerException

The compiler automatically assigns an initial value to a class member of a primitive type, even if the variable is not assigned. For example, int is 0, Boolean is false, so when we use a primitive type, Nullpointerexceptions do not occur.

When using the wrapper class, we should pay attention to this problem. We should not forget the difference between the wrapper class and the basic type because of the syntax sugar of autounboxing.

A NullPointerException occurs when you use a wrapper class without assigning it an explicit or auto-boxing value, and when you extract the value or use the value auto-unboxing.

conclusion

This article will help you to fully understand the mechanism of automatic unpacking and avoid common mistakes and risks.

This is the basis, no matter how far you go, you need to review in time, make up, and so on in the work need to make up, will miss a lot of opportunities.

The purpose of this series is to help you systematically and completely lay a solid foundation and gradually deepen your learning. If you are already familiar with these, please do not hesitate to give your comments and point out the problems, we will do better together!

The article is sent to the wechat public number: Android evolution, welcome to pay attention to, the first time to get the new article.