前言:自动装箱和拆箱是JDK1.5提供的新特性,大致是由编译器自动帮助完成基本类型和基本类型的包装类之间的转换。
好吧,知道了自动装箱的大致意思之后,我们先来复习一个有关基本类型以及包装类的概念:
基本类型:int,double,float,long,boolean,char,btye,short
基本类型包装类:Integer,Double,Float,Long,Boolean,Char,Byte,Short
两者有什么差别,包装类是类,是对象,而基本类型是有值的“变量”,再细化一点,就是包装类的实例(对象)创建在堆上,而基本类型创建在栈上,这是JAVA为了程序的速度考虑做出的优化,那么既然是创建在栈上,所以基本类型都有大小的规定(每种基本类型所占的字节是一定的)。
扯句题外话,那么为什么java的设计者要设计出包装类呢?我的理解是既然java作为一门面向对象的语言,那么包装类作为类,可以容纳更多的信息,可以提供更多的操作。另外,包装类都实现了Comparable接口,可以实现对象之间的比较,所以包装类之间的比较尽量用compareTo,而不是><=这些运算符。切记。
在JDK1.5之前,我们要实现基本类型和包装类之间的转换,大多是通过包装类提供的方法,Integer i = Integer.valueOf(5)或者int j = i.intValue()来做互相转换的。JDK1.5之后,编译器会在我们做赋值操作(这里所说的赋值操作不包括构造函数,我们后面会讲)的时候帮我们自动完成基本类型和包装类之间的相互转换。
上面我们所说的赋值操作,基本上可以分为两种情况,一种是显式赋值,另一种是隐式赋值:
显式赋值,我们可以理解为有赋值符号出现的情况,比如,Integer i = 11;这里编译器会自动的帮你完成把11这个int的基本类型装箱成包装类实例的工作,这样就不用我们再手动的转换了。
隐式赋值,就是没有复制符号出现的情况,这点我们很多人经常忽略,比如方法调用时候的参数传递,比如我们有一个接受Integer类型参数的方法(void method(Integer i)),我们在调用的时候可以直接传递一个基本类型的实参进去(method(5)),这时候编译器会自动的将我们传递进去的实参装箱成包装类,从而完成对方法的调用。很简单的一个过程,里面的原理却不简单,这是因为,方法调用的时候,在内存中的栈上,首先会为方法的形参创建内存区域,然后将我们传递进去的实参赋值给形参,看到没,就是在这时,发生了赋值操作,才会让编译器完成装箱操作。当然,方法执行完后,栈上有关形参的内存区域会被回收。
还有我们要记住一点,就是编译器的装箱/拆箱原则,就是基本类型可以先加宽(比如int转换为long),再转变成宽类型的包装类(Long),但是不能转变成宽类型的包装类型。
比如我们有个方法 void method(long l),我们通过method(5)是可以调用该方法的,因为int类型的5被加宽成了long类型;但是如果这个方法变成void method(Long l),我们通过method(5)就不能调用它了,因为int类型的5是不能直接转换成Long包装类的。切记。
最后还有一个值得注意的地方,就是上面我们提到的显示赋值的情况下,比如Integer i = 11的情况,实际上在源码中是调用到了Integer的静态方法valueOf(),在这一块,java的设计者采用了cache pool的设计模式,在Integer这个类里面有一个长度为256的静态Integer数组,里面存储了从-128到127的Integer对象,当我们传递进去的参数在这个范围之内时,这个方法会返回该数据中实现保存的Integer对象实例,只有超过该范围时,才会返回new出来的Integer对象实例。
public static Integer valueOf(int i) {
return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}
所以会出现下面代码的情况:
Integer i = Integer.valueOf(124);
Integer j = Integer.valueOf(124);
System.out.printIn("result 1:" + (i==j));
Integer x = Integer.valueOf(266);
Integer y = Integer.valueOf(266);
System.out.println("result 2:" + (x==y));
结果:result 1:true
result 2:false
PS:==符号比较的基本类型的值,对对象类型而言,比较的是对象的内存地址。