自动装箱、自动拆箱
自动装箱:把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()、hashCode()、getClass()、equals()等方法。
如下:
Integer a=3;//这是自动装箱
其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(int i)返回一个表示指定int值的Integer对象,那么就变成这样:
Integer a=3; => Integer a=Integer.valueOf(3);
拆箱:跟自动装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为基本类型的数据。
如下:
int i = new Integer(2);//这是拆箱
编译器内部会调用int intValue()返回该Integer对象的int值
注意:自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。
集合框架中必须用包装类,不会自动装箱。
问题:自动拆箱和调用构造函数初始化的对象有区别
可以看一下valueOf()的实现
public static Integer valueOf(int i) { if(i >= -128 &&i <=IntegerCache.high) //如果i在-128~high之间,就直接在缓存中取出i的Integer类型对象 return IntegerCache.cache[i + 128]; else return new Integer(i); //否则就在堆内存中创建 }
也就是说,这个东西是有缓存的,同理Char、Long也有缓存(-XX:AutoBoxCacheMax=<size>可以调整)
如果你将两个在缓存区间内的原始值赋给一个包装类型的变量,那么他们的内存地址完全一致。
这时,如果无论你怎么判断等价性,这两者都是等价的。
但是,如果这个原始值超出了缓存范围,或者使用了new Integer(i)这种显式的构造函数,那么这两者的内存地址就是不相同的。
这个不相同,通过equals()和hashcode()是看不出来的,因为他们在包装类里被重写了。
唯一能看出这两个区别的,就是A==B。
Integer a = new Integer(10086); Integer b = new Integer(10086); System.out.println(a==b); //结果:false Integer a = new Integer(2); Integer b = new Integer(2); System.out.println(a==b); //结果:false Integer a = 2; Integer b = 2; System.out.println(a==b); //结果:true Integer a = 114514; Integer b = 114514; System.out.println(a==b); //结果:false Integer a = 114514; Integer b = 114514; System.out.println(a.equals(b)); //结果:true