自动装箱和拆箱的原理

我们在面试中经常会被问到什么是自动装箱和拆箱,今天,我就通过代码来讲解一下自动装箱背后的原理

学过Java的都知道,自动装箱呢,就是将基本数据类型自动转换成Integer、Character、Long、Double、Boolean这些包装器类型,那么它们背后的原理是什么呢?我们用下面这些代码示例来做讲解:

首先来看这个例子:

public class Main {public static void main(String[] args) throws IOException {Integer a = 10;Integer b = 10;Integer c = 200;Integer d = 200;System.out.println(a == b);System.out.println(c == d);}
}

我们可以看到输出的结果:

一个是true,一个是false,是不是很奇怪呢?明明两个对象指代的值是相同的,为什么最后的结果不同呢?

这里我们就可以引出来自动装箱的原理了~

如果我们对这些代码进行反编译的话,我们就会发现,编译器在底层自动调用了Integer类的valueOf()方法,那么什么是valueOf()方法呢?我们来点进去看看源码:

@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {return i >= -128 && i <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[i + 128] : new Integer(i);
}

这段代码是什么意思呢?

简单来说:就是,在Integer类中本身其实存在一个cache机制,它本身是一个常量,存在于Java的方法区中,当我们给定的整数在[-128,127]之间的话,那么会直接从cache中返回对应的Integer类的引用(或者说地址),如果整数的大小不在这个范围内的话,那么valueof()方法会新new一个Integer类型的对象,并返回。

因此,200大于127,所以每一次返回的包装类型的地址都是不一样的,我们分析出结果原因了~

同样地,我们来看看Double和Boolean类型的自动装箱的背后原理

我们用类似的例子:

public class Main {public static void main(String[] args) throws IOException {Double a = 1.0;Double b = 1.0;Double c = 2.0;Double d = 2.0;System.out.println(a == b);System.out.println(c == d);}
}

运行之后,我们可以看到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ftn8Hq81-1648383130086)(C:\Users\12526\AppData\Roaming\Typora\typora-user-images\image-20220327184938674.png)]

两个返回的都是false,我们同样去查看Double的源码:

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

我们可以看到,每一次valueof都会new一个包装类对象,因此每一次对象的引用地址都是新的,所以两个进行比较的话,是不同的

接下来就是Boolean类型:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.HashMap;public class Main {public static void main(String[] args) throws IOException {Boolean a = true;Boolean b = true;Boolean c = false;Boolean d = false;System.out.println(a == b);System.out.println(c == d);}
}

话不多说:上结果

可以看到,用Boolean包装类型,返回的结果居然都是true,惯例,去看valueof方法

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

我们在源码中发现了两个变量TRUE和FALSE,这两个是个什么东西呢?我们往上面找

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

原来是两个final关键字修饰的静态变量,这样说的话,那么这两个变量的地址都是不变的,也就是说,我们valueof里面return的对象的引用都是相同的~

剩下的Character、Short、Long、Byte基本上和Integer的valueof是类似的,Double、Float的是类似的,Boolean的valueof是单独一组的

以上就是自动装箱的原理了~

接下来我们就讲讲什么是自动拆箱,同样用代码来做讲解,看以下的示例:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.HashMap;public class Main {public static void main(String[] args) throws IOException {Integer a = 1;Integer b = 2;Integer c = 3;Long g = 3L;int int1 = 12;int int2 = 12;Integer integer1 = new Integer(12);Integer integer2 = new Integer(12);Integer integer3 = new Integer(1);System.out.println("c==(a+b)"+(c == (a+b)));System.out.println("g==(a+b)"+(g == (a+b)));System.out.println("c.equals(a+b)"+c.equals(a+b));System.out.println("g.equals(a+b)"+g.equals(a+b));System.out.println("int1 == int2"+(int1 == int2));System.out.println("int1 == integer1"+(int1 == integer1));System.out.println("integer1 == integer2"+(integer1 == integer2));System.out.println("integer3 == a"+(integer3 == a));}
}

在这里我们先铺垫一下==和equals的区别

对于==来说,又分为了基本数据类型和引用数据类型(包括包装类)还有拆箱(这个等会说)

  • 基本数据类型:比较的是值是否形同
  • 引用数据类型:比较的是引用是否相同

对于equals来说,也有两种情况,一种是默认情况,还有一种是重写的情况

  • 默认情况:比较的是引用是否相同
  • 重写情况:有些类在内部对Object类的equals方法进行了重写,比如String类和Integer类,都对该方法进行了重写,因此这种情况下比较的就是值是否相同

我们进到Integer的equals看一看:

public boolean equals(Object obj) {if (obj instanceof Integer) {return this.value == (Integer)obj;} else {return false;}
}

会发现内部使用的是用进行比较,这里号两边一个是基本数据类型,一个是Integer包装类型,因此会进行一个自动拆箱的过程,编译器会调用Integer的intValue()方法,然后比较两个的值,所以这里的equals比较的就是值

其余和Integer同类型的包装类内部有各自对equals方法的重写方式,例如Long:

public boolean equals(Object obj) {if (obj instanceof Long) {return this.value == (Long)obj;} else {return false;}
}

铺垫完了==和equals的不同,我们就可以继续来分析自动拆箱了~

  1. c == (a+b):这里,== 左边是一个包装类型,右边是两个包装类型之和,我们之前说==如果左右两边是包装类型,那么比较的是引用,但是这里很特殊啊,这里右边是一个操作数表达式,因此这里比较的就是值了
  2. g == (a+b):同样的,这里==右边的是一个操作数表达式:因此呢,比较的是值,所以返回true
  3. c.equals(a+b):这里用到了equals,equals又比较特殊了,我们说过,Integer和String是重写了equals的,内部是使用了intValue转换成int基本数据类型的,因此比较的是值,而这里由于存在操作数表达式,因此会触发一个自动拆箱和装箱的过程,a和b各自先拆成int类型进行求和,然后编译器会再调用valueof,对求和后的结果进行装箱,然后再回到equals内部比较,由于我们的c是一个Integer类型,我们根据上面对Integer源码的分析可以看到,它内部的equals会判断这个传入进来的类型是否属于Integer,如果也是Integer类型的话,那么就比较值了,所以最后的结果就是true
  4. g.equals(a+b):这个和上面差不多,也会触发自动拆箱和装箱的过程,但是呢,这里的g是一个Long包装类型,它的内部也重写了equals,不过它判断的是,传入进来的参数是否是Long类型,如果不是Long包装类型,那么直接就返回false了
  5. int1 == int2:这两个都是基本数据类型,那么比较的就是值了,没什么好说的
  6. int1 == integer1:这里==左边的是基本数据类型,右边的是包装类型,这个时候,会触发编译器的自动拆箱,调用Integer的intValue方法,因此比较的是值,返回的是true
  7. integer1 == integer2:我们在上面分析和equals的区别的时候讲过,当左右两边都是包装类型的时候,比较的就是引用,由于这里的integer1和integer2都是new出来的对象,两个的地址肯定是不同的,所以返回的就是false
  8. integer3 == a:这里integer3是一个new出来的对象,而a是一个存在于Integer包装类型cache中的常量,他们两个在内存中的位置是不一样的,因此返回false

以上就是关于自动装箱和自动拆箱的原理的解析了~

下一节,我们就来讲讲什么是Java的方法区以及Java的内存模型

自动装箱和拆箱的原理相关推荐

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

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

  2. Java 自动装箱与拆箱的实现原理

    2019独角兽企业重金招聘Python工程师标准>>> 什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过 ...

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

    什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱. ...

  4. java自动装箱怎么实现_Java 自动装箱与拆箱的实现原理

    什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱. ...

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

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

  6. 底层原理_自动装箱与拆箱底层原理

    1.自动装箱与拆箱 Java中的数据类型分为两大类,基本数据类型与引用数据类型.Java中共提供了八种基本数据类型,同时提供了这八种基本数据类型对应的引用数据类型. 自动装箱:基本数据类型的数据自动转 ...

  7. Java的知识点20——包装类基本知识、包装类的用途、自动装箱和拆箱、包装类的缓存问题

    包装类基本知识 将基本数据类型存储到Object[]数组或集合中的操作 包装类均位于java.lang包 "数字型"都是java.lang.Number的子类.Number类是抽象 ...

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

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

  9. Java自动装箱与拆箱

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

最新文章

  1. 在C#后端处理一些结果然传给前端Javascript或是jQuery
  2. Win2008 r2 下修改mysql data目录的方法
  3. Vue007_ 表单输入绑定
  4. 后端技术:Java代码优秀案例,一定对你有提升!
  5. 微信小程序实战–集阅读与电影于一体的小程序项目(八)
  6. tableau 自定义省份_在Tableau中使用自定义图像映射
  7. poj 1715 Hexadecimal Numbers 排列组合
  8. abaqus推荐用哪一版本的_微信拍一拍怎么用? 微信拍一拍功能在哪怎么拍别人...
  9. 最大堆MaxHeap和最小堆MinHeap的实现(转)
  10. 卷积神经网络——第一周 卷积神经网络基础——第三部分
  11. 带参数的RedirectToAction
  12. 管理感悟:先做难度大的工作
  13. 2018华为软件精英大赛
  14. Elasticsearch中间隔查询slop原理
  15. 67 2020:我的总结和给圈友的话【2020-12-31 0003】
  16. 3天搞定的小型B/S内部管理类软件定制开发项目【软件开发实战10步骤详解】
  17. ConcurrentHashMap1.8 源码分析
  18. android安卓-开源框架汇总
  19. 一文教你分清持续集成,持续交付,持续部署
  20. 联网下载jar包导入本地Maven库

热门文章

  1. linux安装rides
  2. h+ admin ui框架
  3. php 随机三位数字,PHP实现生成随机码
  4. php 自动生成12位数字_php生成12位随机密码
  5. java算法(1)---余弦相似度计算字符串相似率
  6. 【阅读整理】An Accurate Skeleton ExtractionApproach From 3D Point Clouds of Maize Plants
  7. 那些年,我走过的场子!
  8. java初级面试题整理汇总-附答案
  9. golang开发android的jni,用纯Golang开发Android与IOS应用
  10. 王子复仇记游戏java_亚运会LOL决赛你可能没有注意到的细节:RNG队员的王子复仇记...