装箱和拆箱

1.int、Integer、String三者的相互转换(不涉及Autoboxing/Auto-Unboxing)

  1. public static void main(String[] args) {
  2.     int num;
  3.     Integer integer;
  4.     String numString;
  5.     
  6.     //(1) int转换为Integer:public static Integer valueOf(int i)
  7.     num = 100;
  8.     integer = Integer.valueOf(num);
  9.     integer = new Integer(num);
  10.     integer = (Integer) num;
  11.     //(2) Integer转换为int:public int intValue()
  12.     integer = new Integer(100);
  13.     num = integer.intValue();
  14.     //(3) Integer转换为String
  15.     integer = new Integer(100);
  16.     numString = integer.toString();
  17.     //(4) String转换为Integer:public static Integer valueOf(String s) throws NumberFormatException
  18.     numString = "100";
  19.     integer = Integer.valueOf(numString);
  20.     //基本数据类型和类是无法相互转换的,除非通过int的包装类进行过渡。
  21.     //(5) int转换为String
  22.     num = 100;
  23.     numString = Integer.toString(num);
  24.     numString = Integer.valueOf(num).toString();
  25.     numString = new Integer(num).toString();
  26.     //(6) String转换为int:public static int parseInt(String s) throws NumberFormatException
  27.     numString = "100";
  28.     num = Integer.parseInt(numString);
  29.     num = Integer.valueOf(numString).intValue();
  30. }

2.int和Integer的关系

Integer是int的封装类(也称为引用类型or封装类型,Warpper),在堆中创建,占用内存和资源较多,但是提供丰富的访问方法;同时作为真正的对象,可以直接放入集合。初始值为null。

int是基本数据类型(原始类型or内置类型,Primitive),在栈中创建,占用内存和资源较少,但是不能作为对象调用其toString()、hashCode()、getClass()、equals()等方法;也不能被直接加入到集合中,初始值为0

传统上,在Java程序中,可以往一个容器类里直接放入一个对象;但是如果打算放入的是一个数字、字符或布尔值的话,就要先加入一个"生成包裹它们的对象"的步骤。 造成这种现象的原因是,在Java语言当中一直存在着两套非常不同的类型系统:

一套是所谓的"引用类型"(Reference Types),包括所有的类和接口。这些类型的数据被看作对象,所以可以用一个Object型的变量来保存。

一套是所谓的"基本类型"(Primitive Types),包括:byte、short、int、long、float、double、char和boolean。这些类型的数据不是对象,因此也不能用Object型的变量来保存。

同时采用这样两套类型系统,可以得到一些性能方面的好处:基本类型的数据不是对象,所以创建得更快、占用的空间更少、收回它们占用的资源也更容易;引用类型则带来了所有对象具有的优点。

3.Boxing和Unboxing

所谓装箱(Boxing),就是把值类型用它们相对应的引用类型包起来,使它们可以具有对象的特质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double。

所谓拆箱(Unboxing),就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为值类型的数据。

在J2SE 5.0发布之前,我们只能手工的处理装箱和拆箱,而现在,编译器可以帮我们自动地完成这些必要的步骤。下面的代码我提供两个版本的装箱和拆箱,一个版本使用手工的方式,另一个版本则交给编译器去完成:

  1. //这两个方法是等价的。其实在jdk5.0后,我们执行autoBoxingUnboxing()方法,编译器会将其自动将转换为形如manualBoxingUnboxing()方法的代码去执行
  2. public static void manualBoxingUnboxing(int i) {
  3.     List<Integer> list = new ArrayList<Integer>();
  4.     list.add(0new Integer(i));
  5.     int a = list.get(0).intValue();
  6.     System.out.println("The value of i is " + a);
  7. }
  8. public static void autoBoxingUnboxing(int i) {
  9.     List<Integer> list = new ArrayList<Integer>();
  10.     list.add(0, i);
  11.     int a = list.get(0);
  12.     System.out.println("The value of i is " + a);
  13. }

自动装箱(Autoboxing),它可以直接把基本类型赋值给封装类型。eg:Integer num = 10; Double d = 2d;

自动拆箱(Auto-Unboxing),它可以把封装类型赋值给基本类型。eg:int num = new Integer(10); double d = new Double(2d);

所谓Autoboxing/Auto-Unboxing,就是把原来需要手工Boxing/Unboxing的工作交给编译器去完成。

4.容易发生错误的地方

(1)基本类型的数组和引用类型数组之间不会自动转化:int[] ints = {1, 2, 3}; Integer[] integers = ints; /* 编译时出错 */

(2)null的转化问题:试图对null进行Auto-Unboxing操作会导致一个"NullPointerException":Integer i = null; int j = i; /* 运行时错误 */

大部分违反了使用"Autoboxing/Auto-Unboxing"机制时,需要遵守的约束的代码,都会造成编译错误。因此,即使并未准确的记住有关的规则,也不难及时发现。只有违背了"不能对null进行Auto-Unboxing操作"的限制时,引发的是运行时异常,需要特别小心。

(3)对于不同类型间的强制类型转换也很可能出现错误,这个在平时的代码编写中要注意积累总结。

5.在自动装箱时对于值从-128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用。如果超过了从-128到127之间的值,被装箱后的Integer对象并不会被重用,即相当于每次装箱时都新建一个Integer对象。

  1. public static void main(String[] args) {
  2.     int j = 1;
  3.     Integer integer = Integer.valueOf(j);
  4.     
  5.     if ((Integer) j == integer) System.out.println("-128~127之间的Integer对应的堆地址相同");
  6.     else System.out.println("-128~127之间的Integer对应的堆地址不相同");
  7.     
  8.     j = 256;
  9.     integer = Integer.valueOf(j);
  10.     if ((Integer) j == integer) System.out.println("-128~127之外的Integer对应的堆地址相同");
  11.     else System.out.println("-128~127之外的Integer对应的堆地址不相同");
  12. }
  13. /*
  14.  * 输出结果:
  15.  * -128~127之间的Integer对应的堆地址相同
  16.  * -128~127之外的Integer对应的堆地址不相同
  17.  * 
  18.  * */

6.对性能的妨碍

由于Autoboxing机制的实质是"自动创建能代表基本类型数据的对象",所以,不可避免的会对性能造成一些妨碍。如果只是利用Autoboxing/Auto-Unboxing机制来保存基本类型的数据(例如把基本类型的数据放到Collection里面之类),这种影响倒还可以忽略,因为这只是把原来需要手工进行的工作自动化了;但是,如果要频繁的借助Autoboxing来给一个引用类型变量赋值,这开销很容易上升到需要加以注意的程度。例如:注意对引用类型的变量使用"++"和"--"运算符的时候,也会创建新的对象,而不是在修改原来对象的状态。例如:

  1. Integer i = new Integer(1); 
  2. Integer j = i; /* 让j、i指向同一对象 */
  3. System.out.println(j == i); /* 目前j、i是同一对象,因此输出"true" */
  4. i++;
  5. System.out.println(j == i); /* 现在j、i是不同对象,因此输出"false" */

这个现象是由于Java里的引用类型是"不可变的(immutable)",即我们不能修改引用类型对应堆地址的值,如果要修改,只是重新创建一个堆地址,然后将引用引到新的堆地址上。在需要大量赋值操作的时候,可以通过适当使用一些基本类型的局部变量来减轻对性能方面的影响。不过,如果性能的瓶颈在于要往一个容器里频繁放入基本类型的数据的话,恐怕就得靠改用一些专门为容纳基本类型的数据而设计的容器类来解决了(例如Jarkata Commons Primitives组件里提供的那些)。

所以不要过分依赖自动装箱与拆箱,您还是必须知道基本数据类型与对象的差异。

你可能感兴趣的:(String,null,Integer,immutable,编译器,Primitive)