Six years ago, WHEN I returned to Luoyang from Suzhou, I sent many resumes and made interviews with many interviewers, but only two or three of them satisfied me. One of them, named Lao Ma, still lives in my mobile phone address book. He threw me over the head with an interview question: tell me the difference between basic type and packaging type.

I was twenty-three years old, in the prime of youth, engaged in Java programming has N years of experience (N < 4), thought that all the interview questions can answer, the result did not expect ah, was “difficult” — luoyang, the desert of the Internet also has technical experts ah. Now looking back, the face involuntarily rose blush of shame: the main reason is that he was too food. Anyway, it’s time to write an article dissecting the difference between basic types and packaging types.

Each of Java’s basic types has a wrapper type. For example, int is wrapped by type Integer, and double is wrapped by type double. There are four main differences between the basic type and the packaging type.

01. Wrapper types can be null, but basic types cannot

This distinction is not to be underestimated; it allows wrapper types to be applied to POJOs, whereas basic types are not.

What is a POJO? Here’s a little bit of explanation.

For example, a POJO is a Plain Ordinary Java Object with only property fields and setter and getter methods.

class Writer {
	private Integer age;
	private String name;

	public Integer getAge(a) {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getName(a) {
		return name;
	}

	public void setName(String name) {
		this.name = name; }}Copy the code

Similar to POJOs, there are DTO (Data Transfer Object), VO (View Object), A PO (Persistant Object, which can be thought of as a Java Object that maps to a table in the database).

So why do poJOs have to use wrapper types for their properties?

“Alibaba Java Development Manual” has detailed instructions, let’s read it aloud (preparation, up).

The database query result may be NULL if the primitive type is used, because automatic unboxing (converting a wrapper type to a primitive type, such as converting an Integer object to an int value) throws a NullPointerException.

02. Wrapper types can be used with generics, but basic types cannot

Generics cannot use base types because there will be a compilation error if they are used.

List<int> list = new ArrayList<>(); // Syntax error, insert "Dimensions" to complete ReferenceType
List<Integer> list = new ArrayList<>();
Copy the code

Why is that? Because generics are type-erased at compile time, only the primitive types are left, which can only be the Object class and its subclasses — the basic type being a special case.

03. Basic types are more efficient than package types

Basic types store specific values directly on the stack, while wrapper types store references in the heap.

Obviously, wrapper types require more memory space than basic types. Without a basic type, it would be cumbersome to go through a new wrapper type every time for frequently used data such as numbers.

03. The values of two wrapper types can be the same, but not equal

The values of two wrapper types can be the same, but not equal. Let’s look at a piece of code to make this clear.

Integer chenmo = new Integer(10);
Integer wanger = new Integer(10);

System.out.println(chenmo == wanger); // false
System.out.println(chenmo.equals(wanger )); // true
Copy the code

When two wrapper types use “==” to determine whether the addresses they point to are equal. The chenmo and wanger variables use the new keyword, causing them to print false when “==”.

Chenmo.equals (wanger) produces true because the equals method internally compares two int values for equality. The source code is as follows.

private final int value;

public int intValue(a) {
    return value;
}
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}
Copy the code

See, chenmo and Wanger are both 10, but they’re not equal. In other words: when the “==” operator is applied to a wrapper type comparison, the result is likely to be different than expected.

04. Automatic packing and unpacking

Now that you have basic types and wrapper types, there are bound to be times when you need to convert between them. The process of converting a basic type to a wrapper type is called boxing. Conversely, the process of converting a wrapper type to a basic type is called unboxing.

Prior to Java SE5, developers had to manually unpack and unpack, such as:

Integer chenmo = new Integer(10);  // Manual packing
int wanger = chenmo.intValue();  // Unpack manually
Copy the code

Java SE5 provides automatic boxing and unboxing to reduce the work of developers.

Integer chenmo  = 10;  // Automatic packing
int wanger = chenmo;     // Automatic unpacking
Copy the code

The above code, decompiled using JAD, looks like this:

Integer chenmo = Integer.valueOf(10);
int wanger = chenmo.intValue();
Copy the code

That is, autoboxing is done with integer.valueof (); Automatic unpacking is done using integer.intValue (). With that in mind, let’s take a look at an interview question he gave me back in the day.

// 1) Basic type and packaging type
int a = 100;
Integer b = 100;
System.out.println(a == b);

// 2) Two packaging types
Integer c = 100;
Integer d = 100;
System.out.println(c == d);

/ / 3)
c = 200;
d = 200;
System.out.println(c == d);
Copy the code

What’s the answer? Do you have a hand up? If you answer correctly, you’ll get a red flower.

The first code, the basic type and the wrapper type ==, at this point b will automatically unbox and directly compare the value to a, so the result is true.

In the second code, both wrapper types are assigned a value of 100, and auto-boxing is performed. What is the result of ==?

We concluded earlier that when applying the “==” operator to a wrapper type comparison, the result is likely to be different than expected. So it’s false, right? But this time it turned out to be true. Isn’t that surprising?

In the third code, the two wrapper types are reassigned to 200, and the autoboxing is still done, so what is the result of ==?

After eating the second piece of code, is not a little suspicious of life, this time the result is true or false? Flip a coin, haha. Let me tell you the result first. False.

Why is that? Why is that? Why is that?

At this point, it’s time to pull out the big guns — analyze the source code.

We already know that autoboxing is done with integer.valueof (), so let’s look at the source code for this method.

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

Could it be IntegerCache? You guessed it!

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        int i = parseInt(integerCacheHighPropValue);
        i = Math.max(i, 127);
        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127; }}Copy the code

A quick glance at this code will tell you all about it. Numbers between -128 and 127 are taken from IntegerCache and compared, so the second code (100 within this range) will be true, and the third code (200 outside this range) will be true, So new comes out with two Integer objects) and the result is false.

After looking at the above analysis, I want you to keep in mind that when auto-boxing is required, if the number is between -128 and 127, the object in the cache is used directly, rather than recreating an object.

Automatic packing and unpacking is a nice feature that saves us a lot of effort as developers, but it can also cause some trouble, such as this code, which has poor performance.

long t1 = System.currentTimeMillis();
Long sum = 0L;
for (int i = 0; i < Integer.MAX_VALUE; i++) { sum += i; }long t2 = System.currentTimeMillis();        
System.out.println(t2-t1);
Copy the code

Sum was declared as a wrapper type Long instead of the basic type Long, so sum += I did a lot of unpacking (sum was unpacked and added to I, and then packed and assigned to sum), so the code took 2986 milliseconds to complete. If you replace “sum” with the basic type “long”, the time is only 554 milliseconds, which is not on the order of magnitude.

05, finally

Thank you for your reading. It’s not easy to be original. If you like it, just click “like”, which will be my strongest motivation for writing. If you think the article is helpful to you, also quite interesting, just follow my public account, thank you.

PS: secretly tell you, the background reply “Java” can also receive the value of 399 yuan Java advanced information, SHH.