Explain Java automatic packing and unpacking principle in detail

  • 2020-07-21 08:19:02
  • OfStack

What are autoboxing and unboxing

Autoboxing is the process by which Java automatically converts primitive type values to corresponding objects, such as converting int variables to Integer objects, or converting Integer objects to int type values, which is called unboxing. Since the packing and unpacking here is an automatic non-human conversion, it is called automatic packing and unpacking. The original types byte, short, char, int, long, float, double and boolean correspond to the encapsulated classes Byte, Short, Character, Integer, Long, Float, Double, Boolean.

Here's an example of the confusion that comes with automatic packing and unpacking


  public class Test { 
    public static void main(String[] args) {   
      test(); 
    } 

    public static void test() { 
      int i = 40; 
      int i0 = 40; 
      Integer i1 = 40; 
      Integer i2 = 40; 
      Integer i3 = 0; 
      Integer i4 = new Integer(40); 
      Integer i5 = new Integer(40); 
      Integer i6 = new Integer(0); 
      Double d1=1.0; 
      Double d2=1.0; 

      System.out.println("i=i0\t" + (i == i0)); 
      System.out.println("i1=i2\t" + (i1 == i2)); 
      System.out.println("i1=i2+i3\t" + (i1 == i2 + i3)); 
      System.out.println("i4=i5\t" + (i4 == i5)); 
      System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));   
      System.out.println("d1=d2\t" + (d1==d2));  

      System.out.println();     
    } 
  }

Please see the output below as you expected it to be.

Output results:

i=i0   true
i1=i2   true
i1=i2+i3    true
i4=i5   false
i4=i5+i6    true
d1=d2 false

Why is that? Keep reading with questions.

Principle of automatic packing and unpacking

During autoboxing, the compiler calls valueOf to convert the primitive type value to the object, and during unboxing, the compiler converts the object to the primitive type value by calling methods such as intValue() and doubleValue().

Understand the principle of automatic packing and unpacking, we take the above question to analyze the Integer automatic packing source code. As follows:


  public static Integer valueOf(int i) {
    // judge i Whether in -128 and 127 , if not in this range, from IntegerCache Gets an instance of the wrapper class. Otherwise, new1 A new instance 
    if (i >= IntegerCache.low && i <= IntegerCache.high)
      return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
  }


  // Use the Hengyuan pattern to reduce object creation (The Hengyuan design pattern is important to understand 1 I think it's the simplest design pattern, which you probably use a lot in projects, but you don't know his name.) 
  private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    // Static methods that are initialized when the class is loaded cache[], Static variables are stored in constant pools 
    static {
      // high value may be configured by property
      int h = 127;
      String integerCacheHighPropValue =
        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
      if (integerCacheHighPropValue != null) {
        try {
          int i = parseInt(integerCacheHighPropValue);
          i = Math.max(i, 127);
          // Maximum array size is Integer.MAX_VALUE
          h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        } catch( NumberFormatException nfe) {
          // If the property cannot be parsed into an int, ignore it.
        }
      }
      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;
    }

    private IntegerCache() {}
  }

Integer i1 = 40; Automatic packing, equivalent to calling ES65en. valueOf(40); Methods.

First, determine whether the value of i is between -128 and 127. If it is between -128 and 127, get the wrapper class of the specified number directly from the ES71en.cache cache. If there is no new creates a new wrapper class.

Internally, IntegerCache implements a static constant array of Integer. When the class is loaded, static static block is executed to initialize Integer objects between -128 and 127 and store them in the cache array. cache is a constant, stored in the method area of java.

Next look at the implementation of java8 basic types of autoboxing code. As follows:


  //boolean Native types are automatically boxed into Boolean
  public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
  }

  //byte Native types are automatically boxed into Byte
  public static Byte valueOf(byte b) {
    final int offset = 128;
    return ByteCache.cache[(int)b + offset];
  }

  //byte Native types are automatically boxed into Byte
  public static Short valueOf(short s) {
    final int offset = 128;
    int sAsInt = s;
    if (sAsInt >= -128 && sAsInt <= 127) { // must cache
      return ShortCache.cache[sAsInt + offset];
    }
    return new Short(s);
  }

  //char Native types are automatically boxed into Character
  public static Character valueOf(char c) {
    if (c <= 127) { // must cache
      return CharacterCache.cache[(int)c];
    }
    return new Character(c);
  }

  //int Native types are automatically boxed into Integer
  public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
      return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
  }

  //int Native types are automatically boxed into Long
  public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
      return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
  }

  //double Native types are automatically boxed into Double
  public static Double valueOf(double d) {
    return new Double(d);
  }

  //float Native types are automatically boxed into Float
  public static Float valueOf(float f) {
    return new Float(f);
  }

Analyzing the source code, it was found that only double and float's autoboxing code did not use caching, each time it was a new object for new, and the other six basic types used caching strategies.

The caching strategy is used because cached objects are frequently used (such as characters, Numbers between -128 and 127), preventing the creation of an instance of this object every time autoboxing occurs.

double and float, on the other hand, are floating point, don't have particularly hot (and frequently used) data, and don't cache as efficiently as the other types.

Now let's take a look at packing and unpacking.


  //1 The one that hasn't been explained is true
  System.out.println("i=i0\t" + (i == i0)); //true
  //2 , int As long as in -128 and 127 The autoboxing objects are all retrieved from the cache true
  System.out.println("i1=i2\t" + (i1 == i2)); //true
  //3 , involving the calculation of Numbers, must be unpacked first int I'm going to add them up, so I don't care if their values are -128 and 127 Between as long as the number 1 The sample for true
  System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));//true 
  // Compare the memory address of the object false
  System.out.println("i4=i5\t" + (i4 == i5)); //false
  //5 With the first 3 Open the box to do the addition operation, contrast is a number, so is true
  System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));//true   
  //double Caching is not used in the boxing operations of the new Double , so false
  System.out.println("d1=d2\t" + (d1==d2));//false

Related articles: