Java为每种基本数据类型都提供了对应的对象类型。在Java SE5之前,如果要生成一个数值为7的Integer对象,代码示例:Integer i = new Integer(7);;Java SE5之后,Java提供了新的语法,简化了基本数据类型对象的使用,我们称之为自动装箱(autoboxing)与拆箱(unboxing)。之前的代码可以简化为Integer i = 7;。
Java提供的自动装箱盒拆箱,是在编译器层实现的。编译之后的字节码仍然是如Java SE5之前的形态。如自动装箱使用对象的valueOf方法实现的,而拆箱使用对象的 xxxValue方法实现的,xxx代表对应的基本数据类型。
自动装箱与拆箱的原理很简单,通常面试会问对象比较的问题,以考察候选人对源码的了解程度。比较典型的案例是使用==来比较对象是否相等。在实际工作中通常使用对象的equals方法,这是没有问题的。==操作是比较对象的地址是否相同,只要两个地址指向的对象不同,==操作就会返回false。
对于基本数据类型的对象来说,由于考虑到有些对象是会经常使用到的,如果频繁的进行对象的创建和回收,会极大的影响系统效率。因此Java规范也规定需要对于经常使用的对象进行缓存。
对于Integer类型,具体实现中使用了IntegerCache类做缓存,缓存了数值在[-128,127]之间的对象,valueOf方法对于缓存的对象会直接返回对象引用,否则会调用Integer的构造函数创建新对象。因此自动装箱对于[-128,127]之间的数,使用==操作时会返回true,其他情况是false。但有一点需要注意,如果是直接调用构造方法生成的对象,永远都是新对象,也就是说==操作都会返回false。
类似的实现,Short、Long、Byte、Integer都是缓存了数值在[-128,127]之间的相应对象;Character对应char类型,没有负数,因此缓存了[0,127]之间的相应对象;Boolean只有两个数值,都做了缓存。
至于Double和Float,他们没有做任何缓存。其原因可能是:
- 由于在某个范围内的整型数值的个数是有限的,而浮点数却不是;
- 很难评估哪些数是经常被使用到,或者说没有像整型数值那样的在实际使用过程中被经常用到的数值;
基于之前的讨论,我们知道定义数值类型可以使用Integer i = new Integer(7);或者Integer i = 7;,两者在使用上主要注意:
- 前者不会触发自动装箱,而后者会触发自动装箱过程;
- 前者直接生成新对象,而后者会根据具体数值,决定是否生成新对象,还是返回缓存对象的引用;
- 从执行效率和资源占用上来看,后者的效果更高,推荐使用;
另外,在实际工作中,我们还是尽量使用equals方法吧,实在是想不到什么时候会用==操作啊 (除了面试的时候:))