理解JDK1.5的自动装箱拆箱

JDK1.5的升级引入了装箱和拆箱概念,简单说就是为了简化书写。

  JDK1.5之前,创建Integer对象是需要这么写的  Integer i = new Integer("3");

  JDK1.5之后,有了自动装箱,创建Integer对象时,我们可以这样写  Integer i = 5;

1 int num = 3;
2 num = num + 4;
3 //这样写在JDK1.5中是没有问题的 4 Integer x = 3; 5 x = x + 4;

 

  这样以来Integer就拥有了和 int 基本类型一样的能力。表面看起来对象变的可以直接进行运算,这对编程来说方便了很多。

装箱:

  由 Integer x = new Integer(3);   简化成:Integer i = 3; 可以理解为java自动帮我们做了 x = new Integer(3)的操作。

拆箱:

  x = x + 4; 对象是不能直接用来进行运算的,所以java会自动的做拆箱的操作,把(x+4)中的 x 自动拆箱(也就是调用Integer的intValue()方法,把x转换为int类型进行运算),当运算完成之后会把运算结果再次使用装箱赋值给 x 。【整个表达式先拆箱运算完成之后再装箱】

Integer的特殊之处

1 Integer a = new Integer(127);
2 Integer b = new Integer(127);
3 System.out.println(a==b);//false
4 System.out.println(a.equals(b));//true
5 
6 Integer c = 127;//JDK1.5以后,自动装箱如果装箱的是一个字节那么该数据会被共享  不会重新开辟空间
7 Integer d = 127;
8 System.out.println(c==d);//true
9 System.out.println(c.equals(d));//true

  通过测试上面代码的测试结果看出,JDK1.5以后Integer自动装箱的数据如果是一个字节,那么该数据会被共享,不会重新开辟新的空间。

  这就与我们上面说的装箱拆箱操作相矛盾了,如果Integer x = 3  真的代替了  Integer x = new Integer(3); 这里已经使用了new,怎么可能会不开辟新的空间呢?

  如果不开辟空间的话,那么共享数据总要存在于一块共享空间之内,难道会像字符串那样维护了一个常量池?

 1     /**
 2      * Cache to support the object identity semantics of autoboxing for values between
 3      * -128 and 127 (inclusive) as required by JLS.
 4      *
 5      * The cache is initialized on first usage.  The size of the cache
 6      * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
 7      * During VM initialization, java.lang.Integer.IntegerCache.high property
 8      * may be set and saved in the private system properties in the
 9      * sun.misc.VM class.
10      */
11 
12     private static class IntegerCache {
13         static final int low = -128;
14         static final int high;
15         static final Integer cache[];
16 
17         static {
18             // high value may be configured by property
19             int h = 127;
20             String integerCacheHighPropValue =
21                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
22             if (integerCacheHighPropValue != null) {
23                 int i = parseInt(integerCacheHighPropValue);
24                 i = Math.max(i, 127);
25                 // Maximum array size is Integer.MAX_VALUE
26                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
27             }
28             high = h;
29 
30             cache = new Integer[(high - low) + 1];
31             int j = low;
32             for(int k = 0; k < cache.length; k++)
33                 cache[k] = new Integer(j++);
34         }
35 
36         private IntegerCache() {}
37     }

  通过查找源码发现,原来Integer类内部维护了一个静态内部类,这是JDK1.5中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。

  Javadoc 详细的说明这个类是用来实现缓存支持,并支持 -128 到 127 之间的自动装箱过程。最大值 127 可以通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改。 缓存通过一个 for 循环实现。从小到大的创建尽可能多的整数并存储在一个名为 cache 的整数数组中。这个缓存会在 Integer 类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。

  通过查看Integer的构造方法发现,没有一个构造方法会从IntegerCache中取值,很显然自动装箱的操作并不是通过new Integer()来完成的。应该是通过其他的方法!

 1     /**
 2      * Returns an {@code Integer} instance representing the specified
 3      * {@code int} value.  If a new {@code Integer} instance is not
 4      * required, this method should generally be used in preference to
 5      * the constructor {@link #Integer(int)}, as this method is likely
 6      * to yield significantly better space and time performance by
 7      * caching frequently requested values.
 8      *
 9      * This method will always cache values in the range -128 to 127,
10      * inclusive, and may cache other values outside of this range.
11      *
12      * @param  i an {@code int} value.
13      * @return an {@code Integer} instance representing {@code i}.
14      * @since  1.5
15      */
16     public static Integer valueOf(int i) {
17         assert IntegerCache.high >= 127;
18         if (i >= IntegerCache.low && i <= IntegerCache.high)
19             return IntegerCache.cache[i + (-IntegerCache.low)];
20         return new Integer(i);
21     }

  通过查看源码发现valueOf(int i)【只有这一个方法,其他的重载方法未操作IntegerCache】中出现了对IntegerCache的操作。看源码我们了解到如果传入的-128<i<127 就会从IntegerCache的cache数组中取出共享数据,如果不在这个范围之内则使用new Integer(i)。

  由此也就彻底明白了装箱拆箱的内幕!

总结:

  > 装箱使用的是valueOf(int i)

  > 拆箱使用的是intValue();

其实这种缓存行为不仅适用于Integer对象,针对所有整数类型的类都有类似的缓存机制。

有 ByteCache 用于缓存 Byte 对象

有 ShortCache 用于缓存 Short 对象

有 LongCache 用于缓存 Long 对象

有 CharacterCache 用于缓存 Character 对象

Byte、Short、Long有固定范围:-128~127,对于 Character范围是:0~127。

除了Integer可以通过参数改变范围外,其他对象都不可以。

 

 

 

 

 

 

 

posted @ 2017-10-19 00:32  Mr·Dragon  阅读(701)  评论(0编辑  收藏  举报