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

1 //自动装箱
2 Integer total = 99;
3
4 //自动拆箱
5 int totalprim = total;

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

这个过程是自动执行的,那么我们需要看看它的执行过程:

public class Main {
public static void main(String[] args) {
//自动装箱
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函数

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

它会首先判断i的大小:如果i小于-128或者大于等于128,就创建一个Integer对象,否则执行SMALL_VALUES[i + 128]。

相当于在缓存中拿到一个Integer对象

首先我们来看看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函数

1 @Override
2 public int intValue() {
3 return value;
4 }

这个很简单,直接返回value值即可。

二、相关问题 
上面我们看到在Integer的构造函数中,它分两种情况:

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

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

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

说这些可能还不是很明白,下面我们来举个例子吧:

 1 public class Main {
2 public static void main(String[] args) {
4 Integer i1 = 100;
5 Integer i2 = 100;
6 Integer i3 = 200;
7 Integer i4 = 200;
9 System.out.println(i1==i2); //true
10 System.out.println(i3==i4); //false
11 }
12 }

代码的后面,我们可以看到它们的执行结果是不一样的,为什么,在看看我们上面的说明。 
1、i1和i2会进行自动装箱,执行了valueOf函数,它们的值在(-128,128]这个范围内,它们会拿到SMALL_VALUES数组里面的同一个对象SMALL_VALUES[228],它们引用到了同一个Integer对象,所以它们肯定是相等的。

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

下面我们来看看另外一个例子:

 1 public class Main {
2 public static void main(String[] args) {
4 Double i1 = 100.0;
5 Double i2 = 100.0;
6 Double i3 = 200.0;
7 Double i4 = 200.0;
9 System.out.println(i1==i2); //false
10 System.out.println(i3==i4); //false
11 }
12 }

看看上面的执行结果,跟Integer不一样,这样也不必奇怪,因为它们的valueOf实现不一样,结果肯定不一样,那为什么它们不统一一下呢? 
这个很好理解,因为对于Integer,在(-128,128]之间只有固定的256个值,所以为了避免多次创建对象,我们事先就创建好一个大小为256的Integer数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。

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

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

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

下面我们进行一个归类:
Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。 
Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。

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

下面我们来看看另外一种情况:

 1 public class Main {
2 public static void main(String[] args) {
4 Boolean i1 = false;
5 Boolean i2 = false;
6 Boolean i3 = true;
7 Boolean i4 = true;
9 System.out.println(i1==i2);//true
10 System.out.println(i3==i4);//true
11 }
12 }

可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象。

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

可以看到它并没有创建对象,因为在内部已经提前创建好两个对象,因为它只有两种情况,这样也是为了避免重复创建太多的对象。

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

上面把几种情况都介绍到了,下面来进一步讨论其他情况。

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

我们先来看看equals源码:

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

我们指定equal比较的是内容本身,并且我们也可以看到equal的参数是一个Object对象,我们传入的是一个int类型,所以首先会进行装箱,然后比较,之所以返回true,是由于它比较的是对象里面的value值。

1 Integer num1 = 100;
2 int num2 = 100;
3 Long num3 = 200l;
4 System.out.println(num1 + num2); //200
5 System.out.println(num3 == (num1 + num2)); //true
6 System.out.println(num3.equals(num1 + num2)); //false

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

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

它必须满足两个条件才为true: 
1、类型相同 
2、内容相同 
上面返回false的原因就是类型不同。

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

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

可以看到运算的时候首先对num3进行拆箱(执行num3的longValue得到基础类型为long的值300),然后对num1和num2进行拆箱(分别执行了num1和num2的intValue得到基础类型为int的值100和200),然后进行相关的基础运算。

我们来对基础类型进行一个测试:

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

就说明了为什么最上面会返回true.

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

陷阱1:

1  Integer integer100=null;
2 int int100=integer100;

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

总结: 
1、需要知道什么时候会引发装箱和拆箱 
2、装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。

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

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

hello world!!!

Java的自动装箱与拆箱(Autoboxing and unboxing)的更多相关文章

  1. 详解Java的自动装箱与拆箱(Autoboxing and unboxing)

    一.什么是自动装箱拆箱 很简单,下面两句代码就可以看到装箱和拆箱过程 //自动装箱 Integer total = 99; //自定拆箱 int totalprim = total; 简单一点说,装箱 ...

  2. Java 自动装箱与拆箱(Autoboxing and unboxing)

    什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象实例的时候,我们会这样: Class a = ...

  3. 转:Java 自动装箱与拆箱(Autoboxing and unboxing)

    转: http://www.cnblogs.com/danne823/archive/2011/04/22/2025332.html 什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing) ...

  4. Java中自动装箱与拆箱

    一.什么是封装类? Java中存在基础数据类型,但是在某些情况下,我们要对基础数据类型进行对象的操作,例如,集合中只能存在对象,而不能存在基础数据类型,于是便出现了包装器类.包装器类型就是对基本数据类 ...

  5. Java的自动装箱和拆箱

    一.什么是装箱?什么是拆箱? 在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料.在Jav ...

  6. Java 包装类 自动装箱和拆箱

    包装类(Wrapper Class) 包装类是针对于原生数据类型的包装. 因为有8个原生数据类型,所以对应有8个包装类. 所有的包装类(8个)都位于java.lang下. Java中的8个包装类分别是 ...

  7. Java的自动装箱和拆箱的简单讲解

     装箱就是把基础类型封装成一个类.比如把int封装成Integer,这时你就不能把他当成一个数了,而是一个类了,对他的操作就需要用它的方法了. 拆箱就是把类转换成基础类型.比如你算个加法什么的是不能用 ...

  8. Java基础——自动装箱与拆箱

    装箱:把基本数据类型转换为对应的包装类类型 //装箱Integer i=Integer.ValuesOf(100);//自动装箱integer i1=100;//能够直接赋值成功的称为自动装箱,也调用 ...

  9. Java SE之装箱与拆箱【基本数据类型的包装类/==与equals方法】

     对象包装器.自动装箱与拆箱   2016/11/30  晚   特点       1.所有的基本类型都有一个包装器类与之对应.[Integer,Boolean,Long,Character,Shor ...

随机推荐

  1. Spring IOC-基于XML配置的容器

    Spring IOC-基于XML配置的容器 我们先分析一下AbstractXmlApplicationContext这个容器的加载过程. AbstractXmlApplicationContext的老 ...

  2. 2、网络并发编程--套接字编程、黏包问题、struct模块、制作简易报头、上传文件数据

    昨日内容回顾 面向对象复习(json序列化类) 对象.类.父类的概念 三大特性:封装 继承 多态 双下开头的方法(达到某个条件自动触发) __init__:对象实例化自动触发 __str__:对象执行 ...

  3. 聊聊DevOps制品管理-不止是存储制品这么简单

    什么是制品? 制品是指由源码编译打包生成的二进制文件,不同的开发语言对应着不同格式的二进制文件:这些二进制文件通常用于运行在服务器上或者作为编译依赖,"制品的管理"是配置管理的重要 ...

  4. SQL Server 索引结构

    索引是数据库的基础,只有先搞明白索引的结构,才能搞明白索引运行的逻辑 本文通过 索引表.数据页.执行计划.IO统计.B+Tree 来尽可能的介绍 SQL 语句中 WHERE 部分,和 SELECT 部 ...

  5. [LeetCode]剑指 Offer 17. 打印从1到最大的n位数

    输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数.比如输入 3,则打印出 1.2.3 一直到最大的 3 位数 999. 示例 1: 输入: n = 1 输出: [1,2,3,4,5,6,7, ...

  6. volatile关键字的相关学习记录

    1:volatile是什么? Volatile是java虚拟机提供的一种轻量级的同步机制,具有 三大特性,分别是:保证可见性.不保证原子性.禁止指令重排 可见性: 概念:每一个线程都有自己的工作内存, ...

  7. for循环例子

    代码 点击查看[ForTest.java]代码 //package com.d; import java.util.Scanner; /** * For循环例子 * @date: 2022.2.24 ...

  8. [杂记]BrainFuck语言及编译器(c++实现)

    BrainFuck语言 极简的一种图灵完备的语言,由Urban Müller在1993年创造,由八个指令组成(如下表).工作机制与图灵机非常相似,有一条足够长的纸带,初始时纸带上的每一格都是0,有一个 ...

  9. 还在被数据分析报告折磨?Smartbi让你解放双手

    数据分析报告贯穿了企业经营的时时刻刻,方方面面. 数据分析报告最常用于汇报分享:团队需要分享.沟通,数据分析师需要洞察数据.分析结果分享给企业领导.团队同事.大众媒体及更多的利益相关方. 数据分析报告 ...

  10. 【windows 访问控制】十一、C# 实操 对象 System.Security.AccessControl 命名空间

    AccessControl 命名空间 结构图 解说: DirectorySecurity=目录ACLFileSecurity=文件ACLFileSystemAuditRule=目录和文件中SACL中的 ...