博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
装箱与拆箱
阅读量:7117 次
发布时间:2019-06-28

本文共 3867 字,大约阅读时间需要 12 分钟。

装箱与拆箱

什么是装箱与拆箱

描述

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

代码描述就是:

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); //200System.out.println(num3 == (num1+num2)); //trueSystem.out.println(num3.equals(num1+num2)); //falseSystem.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()方法。

参考文章

详解Java的自动装箱与拆箱(Autoboxing and unboxing)
深入剖析Java中的装箱和拆箱

转载于:https://www.cnblogs.com/zhangmiao14/p/9746301.html

你可能感兴趣的文章
iOS学习笔记20 地图(二)MapKit框架
查看>>
深度理解React项目的服务端渲染改造
查看>>
当linux报 “-bash: fork: 无法分配内存”
查看>>
同步/异步
查看>>
使用CSS实现逼真的水波纹点击效果
查看>>
[番外篇]k位精巧数
查看>>
Android-Application详解
查看>>
echarts 的使用
查看>>
iOS超全面试题,面试前看一看,不错&lt;上篇&gt;
查看>>
四个提升服务器数据安全的方法
查看>>
分分钟解决MySQL查询速度慢与性能差
查看>>
设计电商平台优惠券系统
查看>>
后来才知道的JavaScript自带函数
查看>>
spring cloud互联网分布式微服务云平台规划分析--spring cloud定时调度平台
查看>>
判断数组是否为非递减数组 Non-decreasing Array
查看>>
我的友情链接
查看>>
ssh免密码登录简略配置
查看>>
VI/VIM实用功能备忘
查看>>
Android中View绘制流程以及invalidate()等相关方法分析
查看>>
导出Windows服务器下的Oracle数据库并导入到Linux服务器下的Oracle数据库中
查看>>