装箱与拆箱

装箱与拆箱

什么是装箱与拆箱

描述

语言描述,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

代码描述就是:

    Integer integer = 100;  //自动装箱
    int i = integer;  //自动拆箱

基本技术类型对应的包装器类型表:

数据类型包装器类型
int(4字节) Integer
byte(1字节) Byte
short(2字节) Short
long(8字节) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(未定) Boolean

如何实现装箱与拆箱

装箱与拆箱的代码


public class IntegerAndInt {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Integer integer = 100; //自动装箱
        int i = integer; //自动拆箱
    }

}

反编译class文件


从反编译得到的字节码内容可以看出,在装箱的时候自动调用了Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

因此,装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的xxxValue方法实现的。

valueOf方法

 public static Integer valueOf(int i) {
     return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
 }

首先判断数值大小,如果数值大于等于128或者小于-128,则创建一个Integer对象返回,否则,则返回SMALL_VALUES的i+128的数值。

接着,查看以下Integer的构造函数:

private final int value;

public Integer(int value) {
    this.value = value;
}

public Integer(String string) throws NumberFormatException {
    this(parseInt(string));
}

Integer定义了一个value变量,创建一个Integer对象时,就会给这个变量初始化。第二个传入的是一个String变量,它会先把它转换成一个int值,然后进行初始化。

再看一下SMALL_VALUES[i+128]是什么:

private static final Integer[] SMALL_VALUES = new Integer[256];

SMALL_VALUES是一个静态的Integer数组对象,也就是说valueOf返回的都是一个Integer对象。
通过分析可以看到装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能。

initValue方法

 @Override
 public int intValue() {
     return value;
 }

intValue方法直接返回了value值。

装箱与拆箱需要注意的一些问题

例子一

public class Main{
    public static void main(String[] args) {
        Integer i1 = 66, i2 = 66, i3 = 166, i4 = 166;
        System.out.println(i1 == i2);//true
        System.out.println(i3 == i4);//false
       }
}

看上面的代码可以发现,两个比较的结果不相同,再结合上面的装箱原理,128~-127的装箱是直接返回的SMALL_VALUES数组中存储的值,所以i1和i2的装箱结果返回的是同一个变量,所以是相等的,而i3和i4是返回的新创建的变量,两个变量是不同的,所以不相等。

例子二

Integer a = new Integer(6);
Integer b = 6; // 将6自动装箱成Integer类型
int c = 6;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较

a是一个创建的Integer对象,而b是自动装箱,是SMALL_VALUES数组中存储的值,所以a和b是不同的对象,不相等,而c是int类型的值,a与c相比是会先拆箱为int类型的6,两个6数值相比,故相等。

例子三

    Integer i1 = new Integer(6);
    Integer i2 = 6;
    System.out.println("i1.equals(i2):"+(i1.equals(i2))); //true

会发现同样的对象,==与equals的结果是不同,先看一下equals源码:

 @Override
 public boolean equals(Object o) {
     return (o instanceof Integer) && (((Integer) o).value == value);
 }

发现equals方法是比较value值相同的,比较的内容的本身。

例子四

Integer num1 = 100;
int num2 = 100;
Long num3 = 200L;
System.out.println(num1+num2); //200
System.out.println(num3 == (num1+num2)); //true
System.out.println(num3.equals(num1+num2)); //false
System.out.println(num3 == (num1+num2)); //true

当一个基本类型数据域封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
而num3.equals(num1+num2)为false的原因是,num1+num2的类型不是Long,所以为false。
Long的equals方法:

 @Override
 public boolean equals(Object o) {
     return (o instanceof Long) && (((Long) o).value == value);
 }

而(num3 (num1+num2))为true,当运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。

例子五

  Integer integer=null;
  int i=integer;

上面的代码可以通过编译,但是在运行时,就会抛出空指针异常。integer是Integer类型的对爱选哪个,可以指向null,但是对integer进行拆箱的时候,就是对一个null对象调用valueOf方发,所以会抛出空指针异常。

注意

Integer、Short、Byte、Charater、Long这几个类的valueOf方法的实现是类似的,Double和Float的valueOf方法的实现是类似的,是没有想Integer一样的SMALL_VALUE数组的。
Double的valueOf方法:

public static Double valueOf(double d) {
    return new Double(d);
}

Float的valueOf方法:

public static Float valueOf(String s) throws NumberFormatException {
    return new Float(FloatingDecimal.getThreadLocalInstance().readJavaFormatString(s).floatValue());
}

还有,Boolean的valueOf方法:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

所以不管对象是不是同一个,只要对象的值时相同的,就是相等的。

5.总结

Java通过自动装箱和拆箱的机制,节省了部分内存开销和创建对象的开销(Integer的128~-127常用值节省开支),提高了效率同时简化了代码,不用每次需要程序员转换类型。在比较数值相等时,尽量使用equals()方法。

参考文章

https://www.cnblogs.com/wang-yaz/p/8516151.html 详解Java的自动装箱与拆箱(Autoboxing and unboxing)
https://www.cnblogs.com/dolphin0520/p/3780005.html 深入剖析Java中的装箱和拆箱

原文地址:https://www.cnblogs.com/zhangmiao14/p/9746301.html