点击上方 Java后端,选择 设为星标

优质文章,及时送达


什么是自动装箱拆箱?很简单,下面两句代码就可以看到装箱和拆箱过程

//自动装箱
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);  //trueSystem.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); //falseSystem.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);//trueSystem.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-yaz/p/8516151.html

Java后端交流群已成立
公众号运营至今,离不开小伙伴们的支持。为了给小伙伴们提供一个互相交流的平台,特地开通了官方交流群。扫描下方二维码备注 进群 或者关注公众号 Java后端 后获取进群通道。
推荐阅读 1. MyBatis-Plus常用 API 全套教程2. 如何用手机抓包?3. 牛逼!Docker从入门到上瘾4. 连夜撸了一个简易聊天室5. 推荐一款 Java 对象映射神器

详解 Java 中的自动装箱与拆箱相关推荐

  1. Java 中的自动装箱和拆箱详解

    自动装箱是 Java 编译器在基本类型和它们对应的包装类之间进行的自动转换.例如,将 int 类型转换为 Integer 类型,将 double 类型转换为 Double 型等等.如果转换以相反的方式 ...

  2. Java中的自动装箱和拆箱

    自动装箱和拆箱 自动装箱和拆箱 自动装箱: 拆箱 1. 为什么要有包装类(或封装类) 2. 基本数据类型与对应的包装类: 3. 类型间的转换 4. 何时发生自动装箱和拆箱 赋值.数值运算时 方法调用时 ...

  3. java的自动装箱和拆箱,Java中的自动装箱和拆箱是什么?

    当我们用任何语言编写程序时,大多数时候我们使用原始数据类型,.但是,在面向对象编程领域,有些情况下原始数据类型会出现不足,而Java就是其中之一.为了克服原始数据类型的缺点,我们 当我们用任何语言编写 ...

  4. java装箱拆箱_Java中的自动装箱与拆箱

    自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...

  5. Java™ 教程(自动装箱和拆箱)

    自动装箱和拆箱 自动装箱是Java编译器在基元类型和相应的对象包装类之间进行的自动转换,例如,将int转换为Integer,将double转换为Double,依此类推,如果转换是另一种方式,则称为拆箱 ...

  6. Java包装类及自动装箱、拆箱

    Java包装类 基本类型 大小 包装器类型 boolean / Boolean char 16bit Character byte 8bit Byte short /16bit Short int 3 ...

  7. 张赐荣: 详解 Java 中的包装类型

    在Java中,包装类型是用于将基本数据类型(int.float.boolean等)转换为对象的类.这些类被称为包装类,它们的名字都是在基本数据类型前面加上"Integer".&qu ...

  8. Java自动装箱与拆箱及其陷阱

    2019独角兽企业重金招聘Python工程师标准>>> 在本文中,笔者向大家介绍下Java中一个非常重要也非常有趣的特性,就是自动装箱与拆箱,并从源码中解读自动装箱与拆箱的原理,同时 ...

  9. java的自动装箱_详解Java 自动装箱与拆箱的实现原理

    详解Java 自动装箱与拆箱的实现原理 发布于 2020-7-4| 复制链接 本篇文章主要介绍了详解Java 自动装箱与拆箱的实现原理,小妖觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小妖 ...

最新文章

  1. leetcode--删除链表的倒数第N个节点--python
  2. Mac上Homebrew的使用
  3. c 连接oracle的参数,[20210203]19c登录连接改变一些参数.txt
  4. 标记化结构初始化语法(C语言)
  5. Github开源!适合初学者的机器学习和深度学习的资料合集
  6. Git 下载很慢问题解决方案
  7. Seek the Name, Seek the Fame POJ - 2752 (理解KMP函数的失配)既是S的前缀又是S的后缀的子串
  8. asp.net学习之再论sqlDataSource 1
  9. 详解OpenCV中的cvCreateMat()函数
  10. Spark源码系列(一)spark-submit提交作业过程
  11. 随笔:《向死而生》---我修的死亡学分
  12. Unity 按钮脚本
  13. LaTeX详细安装步骤和简明教程
  14. 修复漏洞显示连接服务器失败,服务器安全狗漏洞补丁失败怎么办
  15. 【Matplotlib:隐藏边框,显示单个网格线,设置刻度间隔,隐藏刻度线】
  16. Chrome 开发者工具 network 显示 Provisional headers are shown 的几种原因
  17. 文字烫金效果html,一种浮雕烫金效果的收缩套标的制作方法
  18. 实现网站流量统计(监控网站流量)
  19. 物联网与AI芯片密不可分 企业加速跑马圈地
  20. 支付宝AR实景红包寻宝攻略

热门文章

  1. 在少儿编程中使用easygui(3):enterbox和multenterbox
  2. Django源码解析:preserved_filters的实现
  3. 22.9.18——论坛
  4. 2005年中国BBS社区100强
  5. C++类与对象(类中的六大默认成员函数)
  6. brew 一直等待_LOL手游转圈怎么办_LOL手游一直转圈怎么办
  7. 家有网瘾少年不要急,五步教你戒网瘾[为本教育]
  8. ansys的kbc_在Ansys中施加阶跃载荷和渐变载荷的方法 | 坐倚北风
  9. 费斯汀格法则及其释义
  10. 无产阶级游戏制作人必须明白的哲理(转)