Java是面向对象语言,在她的世界里充满了对象,对象让我们更好地彼此沟通,但是在java中有一块区域是不具备对象特性的,就是基本数据类型(boolean,byte,short,character,int,long,double,float),为了能让基本数据类型也具备对象的特性,Java为每个基本类型提供了包装类,如int的包装类Integer。这样我们就可以像操作对象那样来操作基本数据类型,如利用toString,pasreInt来进行类型转换。另外包装类带来的好处是允许在集合类(list,HashMap)来操作基本数据类型。
在JDK5.0之前是不允许直接将基本数据类型的数据直接赋值给其对应地包装类的,如:
Integer i = 5;
但是在JDK5.0中支持这种写法,因为编译器会自动将上面的代码转换成如下代码:Integer i=Integer.valueOf(5);
这就是Java的装箱,由此会产生一个很有意思的现象:
Integer i1 =5; Integer i2 =5; Integer i3 =500; Integer i4 =500; System.out.println(i1 == i2); // true System.out.println(i3 == i4); // false
为什么这两个输出地结果会是不一样的呢,首先我们要从Java的自动装箱说起,自动装箱时调用的valueOf(int)方法的源码如下:
public static Integer valueOf(int i) { final int offset = 128; if (i >= -128 && i <= 127) { // must cache return IntegerCache.cache[i + offset]; } return new Integer(i); } private static class IntegerCache { private IntegerCache(){} static final Integer cache[] = new Integer[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Integer(i - 128); } }
从上面的代码我们可以清晰地看到当 i>=-128 && i<=127时方法返回的IntegerCache中的cache数组的值,cache数组则是存储在缓存中,这样做的目的是为了提高性能,当你大量使用new Integer(5)时就不用大量地创建对象实例,而是共用cache数组已经创建好的Integer对象实例,这也是享元模式的体现(String类是通过字符串常量池来实现的)。
与装箱对应地,JDK5.0提供了自动拆箱,代码如下:第一行先进行自动装箱,生成Integer对象实例 i,再把 i 赋值给 int类型的 j 时,编译器会做如下操作:Integer i =5; int j = i;
把 i 的int类型数值赋值给 j,这样就完成了自动拆箱的过程。Java的拆箱也产生了一个比较有意思的情况,如:int j = i.intValue();
解释起来也很简单,i1 != i2比较的是两个对象的引用,而i1 <= i2和i1 >= i2比较的是拆箱之后的基本数据类型的数值。Integer i1=new Integer(5); Integer i2=new Integer(5); System.out.println((i1!=i2)&&(i1<=i2)&&(i1>=i2)); // true
装箱和拆箱在重载中的应用,如下:
public class Test{ public static void main(String[] a) { int i = 5; print(i); } public static void print(Integer i) { System.out.print("Integer"); } public static void print(long l) { System.out.print("long"); } }
上面输出“long”,也就是说编译器选择了对 i 进行加宽操作,让 int 类型的 i 转换成 long类型,而不是对 i 进行装箱为 Integer,故加宽>装箱,那var-arg(可变参数列表)呢,如果你去测试会发现装箱>var-arg。
说到这里我们基本上已经介绍完了Java的包装类已经相关地自动装拆箱,在更好地了解了包装类以及自动装拆箱之后,有助于我们日常开发中的代码优化。
解读java的包装类
最新推荐文章于 2023-04-17 18:00:39 发布