Fork me on GitHub

Java包装类及其拆箱装箱


在java中,数据类型总共可分为两大种,基本数据类型(值类型)和引用数据类型(类类型).基本类型的数据不是对象,所以对于要将数据类型作为对象来使用的情况,java提供了相对应的包装类。对于8种基本数据类型的包装类分别是:

1
2
3
4
5
6
7
8
int     Integer
char Character
float Float
double Double
byte Byte
short Short
long Long
boolean Boolean
  1. 所谓装箱,就是把基本数据类型用他们相对应的引用类包起来,使他们可以具有对象的性质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double,等等。所谓拆箱,就是跟装箱的方向相反,将IntegerDouble这样的引用数据类型的对象重新简化为值类型的数据.J2SE5.0后提供了自动拆箱与装箱的功能,此功能事实上是编译器来帮您的忙,编译器在编译期间以您所编写的方法,决定是否进行装箱或拆箱动作.

    • 自动装箱过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与他相同类型的包装中。
    • 自动拆箱过程:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue()doubleValue()方法

      1
      2
      3
      4
      5
      6
      Integer i = 100;    // 没有通过使用new来显示建立,java自动完成
      int j = i; // 自动拆箱

      int i = 10;
      Integer j = new Integer(i); // 手动装箱操作
      int k = j.intValue(); // 手动拆箱操作
  2. 然而在Integer的自动装拆箱会有细节值得注意

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static void main(String[] args){
    Integer a = 100;
    Integet b = 100;

    Integer c = 200;
    Integer d = 200;

    System.out.println(a == b); // 1
    System.out.println(a == 100); // 2

    System.out.println(c == d); // 3
    System.out.println(c == 200); // 4
    }

在Java中,“==”是比较的引用而不是值,自动装箱后,abcd都是Integer这个Object,因此“==”比较的是其引用。
按照常规思维,1和3都应该输出false.但结果是

1
2
3
4
true
true
false
true
  • 结果2,4,是因为ac进行了自动拆箱,因此其比较是基本数据类型的比较,就跟int比较时一样。“==”在这里是比较他们的值而不是引用

  • 对于结果1,虽然比较的时候,还是比较对象的引用,但是自动装箱时,java在编译的时候,Integer a = 100;被翻译成->Integer a = Integer.valueOf(100).关键就在于这个valueOf()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static Interger 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 = new Integer(i-128);
}
}

根据上面的jdk源码,java为了提高效率,IntegerCache类有一个数组缓存了值从-128-127的Integer对象。当我们调用Integer.valueOf(int i)时,当i大于-128小于127时,会直接从这个缓存中返回一个对象,否则就new一个Integer对象。

例子1:

1
2
3
4
5
6
7
8
9
10
11
public class Test{
public static void main(String arts[]){
Integer m = new Integer(5);
Integer n = new Integer(5);
System.out.println(m == n);

m = m-1;
n = n-1;
System.out.println(m == n);
}
}

输出结果:

1
2
false
true

原因:m,n都是new出来的对象,内存地址不一样,所以第一次m==n比较的reference不一样。但是,m=m-1首先
进行了自动拆箱m.intValue,相减后再进行装箱操作:m = Integer.valueOf(m.intValue-1),而m,n
都在-128-127的范围,所以自动装箱后,根据上文所述,都是同一个object的引用。因此第二次输出true

例子2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestWhile{
public static void main(String[] args){
Integer i = 0; //3
Integer j = 0; //4

//Integer i = new Integer(0); //5
//Integer j = new Integer(0); //6

while(i <= j & i>=j & i!=j) // 7
{
System.out.println("0000");
}
}
}

哪一行是拆箱?while循环里的条件看似不成立,可为什么可以运行(去掉第5,6行的注释)

这种情况下,循环不能运行。对于Integer类型,<,<=,>,>=操作将导致拆箱操作
==,!=比较的是对象的引用

注释3,4,使用5,6时,循环条件成立,while(i<=j & i >= j & i != j)成为无穷循环,不断输出

1
2
3
4
5
6
7
/*
* 原题:循环者的诅咒
* 请提供一个对i的声明,将下面的循环转变为一个无限循环
*/
while(i <= j && j <= i && i!= j){
}
}

总结:对于要比较Object的值,最稳妥的方法还是调用equal()这个方法,而不是使用==,因为会比较其引用

1
2
3
4
5
6
public boolean equals(Object obj){
if(obj instanceof Integer){
return value == ((Integer)obj).intValue();
}
return false;
}
坚持原创技术分享,您的支持将鼓励我继续创作
-------------本文结束感谢您的阅读-------------
0%