关于java:详解-Java-中的自动装箱与拆箱5000+字看了不懂你打我

44次阅读

共计 4835 个字符,预计需要花费 13 分钟才能阅读完成。

什么是主动装箱拆箱?​

很简略,上面两句代码就能够看到装箱和拆箱过程

// 主动装箱
Integer total = 99;
// 主动拆箱
int totalprim = total;

简略一点说,装箱就是主动将根本数据类型转换为包装器类型;拆箱就是主动将包装器类型转换为根本数据类型。

上面咱们来看看须要装箱拆箱的类型有哪些:

这个过程是主动执行的,那么咱们须要看看它的执行过程:

反编译 class 文件之后失去如下内容:

1 javap -c StringTest

Integer total = 99;

执行下面那句代码的时候,零碎为咱们执行了:

Integer total = Integer.valueOf(99);

int totalprim = total;

执行下面那句代码的时候,零碎为咱们执行了:

int totalprim = total.intValue();

咱们当初就以 Integer 为例,来剖析一下它的源码:

1、首先来看看 Integer.valueOf 函数

public class Main {public static void main(String[] args) {
    // 主动装箱
    Integer total = 99;

    // 自定拆箱
    int totalprim = total;
    }
}

它会首先判断 i 的大小:如果 i 小于 -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));
}

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

上面看看 SMALL_VALUES[i + 128]是什么货色:

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

它是一个动态的 Integer 数组对象,也就是说最终 valueOf 返回的都是一个 Integer 对象。

所以咱们这里能够总结一点:装箱的过程会创立对应的对象,这个会耗费内存,所以装箱的过程会减少内存的耗费,影响性能。

2、接着看看 intValue 函数

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

这个很简略,间接返回 value 值即可。

相干问题

下面咱们看到在 Integer 的构造函数中,它分两种状况:

1、i >= 128 || i < -128 =====> new Integer(i)
2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]

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

SMALL_VALUES 原本曾经被创立好,也就是说在 i >= 128 || i < -128 是会创立不同的对象,在 i < 128 && i >= -128 会依据 i 的值返回曾经创立好的指定的对象。

说这些可能还不是很明确,上面咱们来举个例子吧:

public class Main {public static void main(String[] args) {

        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println(i1==i2);  //true
        System.out.println(i3==i4);  //false
    }
}

代码的前面,咱们能够看到它们的执行后果是不一样的,为什么,在看看咱们下面的阐明。

1、i1 和 i2 会进行主动装箱,执行了 valueOf 函数,它们的值在 (-128,128] 这个范畴内,它们会拿到 SMALL_VALUES 数组外面的同一个对象 SMALL_VALUES[228],它们援用到了同一个 Integer 对象,所以它们必定是相等的。

2、i3 和 i4 也会进行主动装箱,执行了 valueOf 函数,它们的值大于 128,所以会执行 new Integer(200),也就是说它们会别离创立两个不同的对象,所以它们必定不等。

上面咱们来看看另外一个例子:

public class Main {public static void main(String[] args) {

        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;

        System.out.println(i1==i2); //false
        System.out.println(i3==i4); //false
    }
}

看看下面的执行后果,跟 Integer 不一样,这样也不用奇怪,因为它们的 valueOf 实现不一样,后果必定不一样,那为什么它们不对立一下呢?

这个很好了解,因为对于 Integer,在 (-128,128] 之间只有固定的 256 个值,所以为了防止屡次创建对象,咱们当时就创立好一个大小为 256 的 Integer 数组 SMALL_VALUES,所以如果值在这个范畴内,就能够间接返回咱们当时创立好的对象就能够了。

然而对于 Double 类型来说,咱们就不能这样做,因为它在这个范畴内个数是有限的。
总结一句就是:在某个范畴内的整型数值的个数是无限的,而浮点数却不是。

所以在 Double 外面的做法很间接,就是间接创立一个对象,所以每次创立的对象都不一样。

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

上面咱们进行一个归类:

Integer 派别:Integer、Short、Byte、Character、Long 这几个类的 valueOf 办法的实现是相似的。

Double 派别:Double、Float 的 valueOf 办法的实现是相似的。每次都返回不同的对象。

上面对 Integer 派别进行一个总结,如下图:

上面咱们来看看另外一种状况:

public class Main {public static void main(String[] args) {

        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;

        System.out.println(i1==i2);//true
        System.out.println(i3==i4);//true
    }
}

能够看到返回的都是 true,也就是它们执行 valueOf 返回的都是雷同的对象。

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

能够看到它并没有创建对象,因为在外部曾经提前创立好两个对象,因为它只有两种状况,这样也是为了防止反复创立太多的对象。

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

下面把几种状况都介绍到了,上面来进一步探讨其余状况。

Integer num1 = 400;
int num2 = 400;
System.out.println(num1 == num2); //true
// 阐明 num1 == num2 进行了拆箱操作
Integer num1 = 100;
int num2 = 100;
System.out.println(num1.equals(num2));  //true

咱们先来看看 equals 源码:

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

咱们指定 equal 比拟的是内容自身,并且咱们也能够看到 equal 的参数是一个 Object 对象,咱们传入的是一个 int 类型,所以首先会进行装箱,而后比拟,之所以返回 true,是因为它比拟的是对象外面的 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

1、当一个根底数据类型与封装类进行 ==、+、-、*、/ 运算时,会将封装类进行拆箱,对根底数据类型进行运算。
2、对于 num3.equals(num1 + num2) 为 false 的起因很简略,咱们还是依据代码实现来阐明:

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

它必须满足两个条件才为 true:

1、类型雷同

2、内容雷同

下面返回 false 的起因就是类型不同。

Integer num1 = 100;
Ingeger num2 = 200;
Long num3 = 300l;
System.out.println(num3 == (num1 + num2)); //true

咱们来反编译一些这个 class 文件:javap -c StringTest

能够看到运算的时候首先对 num3 进行拆箱(执行 num3 的 longValue 失去根底类型为 long 的值 300),而后对 num1 和 mum2 进行拆箱(别离执行了 num1 和 num2 的 intValue 失去根底类型为 int 的值 100 和 200),而后进行相干的根底运算。

咱们来对根底类型进行一个测试:

int num1 = 100;
int num2 = 200;
long mum3 = 300;
System.out.println(num3 == (num1 + num2)); //true

就阐明了为什么最下面会返回 true.

所以,当 == 运算符的两个操作数都是 包装器类型的援用,则是比拟指向的是否是同一个对象,而如果其中有一个操作数是表达式(即蕴含算术运算)则比拟的是数值(即会触发主动拆箱的过程)。

陷阱 1:

Integer integer100=null;
int int100=integer100;

这两行代码是齐全非法的,齐全可能通过编译的,然而在运行时,就会抛出空指针异样。其中,integer100 为 Integer 类型的对象,它当然能够指向 null。但在第二行时,就会对 integer100 进行拆箱,也就是对一个 null 对象执行 intValue() 办法,当然会抛出空指针异样。所以,有拆箱操作时肯定要特地留神封装类对象是否为 null。

总结

1、须要晓得什么时候会引发装箱和拆箱

2、装箱操作会创建对象,频繁的装箱操作会耗费许多内存,影响性能,所以能够防止装箱的时候应该尽量避免。

3、equals(Object o) 因为原 equals 办法中的参数类型是封装类型,所传入的参数类型(a)是原始数据类型,所以会主动对其装箱,反之,会对其进行拆箱

4、当两种不同类型用 == 比拟时,包装器类的须要拆箱,当同种类型用 == 比拟时,会主动拆箱或者装箱

起源:https://www.cnblogs.com/wang-…

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)

2. 别在再满屏的 if/ else 了,试试策略模式,真香!!

3. 卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.6 正式公布,一大波新个性。。

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0