JavaSE的自动装箱和自动拆箱

原创 2017年05月30日 17:24:32

回顾一下Java基础数据类型:

Java数据类型

基础类型 字节 包装类型
int 4字节 Integer
byte 1字节 Byte
short 2字节 Short
long 8字节 Long
float 4字节 Float
double 8字节 Double
char 2字节 Character
boolean 未定 Boolean

Java属于面向对象语言那么为什么会出现非对象类型数据(基础类型),因为基础数据类型是的虚拟机的运行速度更快而且占用内存更少。详情内容可以参见:Java为什么需要保留基本数据类型

为什么要有装箱&拆箱

在JavaSE5之前我们创建爱你Integer对象:

Integer i = new Integer(10);

从JavaSE5提供了自动装箱的特性时,我们可以更简单的创建基础类型的对象:

Integer a = 10;
int b = a;

从上面的代码我们可以简单的看出装箱、拆箱的操作:
Integer a = 10; 我们将10【装箱】生成Integer对象。
int b = a; 我们将Integer【拆箱】转成int基础类型

装箱和拆箱是如何实现的

我们这里先写一个简单的类,然后反编译看看它的字节码文件

public class Main {
    public static void main(String[] args) {
        Integer a = 10;
        int b = a;
    }
}

反编译出来的字节码文件:

Main字节码.jpg

结论:

装箱操作:

Integer a = 10;
//实际执行的是Integer a = Integer.valueOf(10);

拆箱操作:

int b = a;
//实际执行的是int b = a.intValue();

其他&扩展

我们先来看一道面试题:

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

内心怀揣自己的真是答案,我们看看下边的源代码:
先看看Integer装箱和拆箱的函数源码:

/**
 * Returns the value of this {@code Integer} as an
 * {@code int}.
 */
public int intValue() {
    return value;
}

/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
  • 拆箱操作:直接返回Integer内的数值
  • 装箱操作:在i大于IntegerCache.low或者i小于IntegerCache.high时返回缓存的Integer对象,否则创建新的Integer对象。
/**
 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
 *
 * The cache is initialized on first usage.  The size of the cache
 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 * During VM initialization, java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * sun.misc.VM class.
 */
private static class IntegerCache {
    /*********/
}

通过源码码我们可以发现,Integer在数据为[-128,127]之间时。使用了IntegerCache 返回缓存中对象的引用,否则new一个新的对象。

看到上面这个答案,有些同学就会想到:除过Integer之前还有其他的基础数据类型,那么其他的类型是否也是专业那个的呢?答案:是也不是。原理想想大家也都明白:

  • Boolean内部有true&false两个静态变量,最后装箱得到的值都是这两个静态变量的引用。
  • Long&Integer&Short&Byte在数值为[-128,127]之间都有Cache。
  • Double&Float则都没有。

所以上面问题的正确答案分别是:true、false。

见识了”==”比较,现在看equals比较结果:
同样我们也先看一道题目:

public class Main {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Long c = 3L;
        System.out.println(c == (a + b));
        System.out.println(c.equals(a + b));
    }
}

然后我们再看看源码:

/**
 * Compares this object to the specified object.  The result is
 * {@code true} if and only if the argument is not
 * {@code null} and is an {@code Integer} object that
 * contains the same {@code int} value as this object.
 *
 * @param   obj   the object to compare with.
 * @return  {@code true} if the objects are the same;
 *          {@code false} otherwise.
 */
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

在Java中我们知道操作”==”的两个数都是数据包装类型对象的引用的话,那么则是用来比较两个引用所指向的对象是不是同一个;而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。为什么呢,因为”==”两边引用数据类型必须一致,要不然无语错误。

所以我们得到上边题目的答案是:true、false。
因为第一次比较实际是先对数据进行拆箱然后比较,所以得到的结果是true;第二次比较实际是先拆箱(两个Integer对象拆箱)后装箱(将拆箱且计算后的数据再装箱),然后同Long对象比较,显然不是同一类型所以得到false。

以上问题及答案都是作者亲自敲出来的,想实际操作同学也可以反编译class文件看看【真相】。

想阅读作者的更多文章,可以查看我的公共号:

振兴书城

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

java中jar包内的类访问jar包内部的资源文件的路径问题

在本地项目中,若我们要访问项目中的资源文件,则一般使用相对路径或者用System.getProperities("user.dir")得到项目根目录,然后再访问资源文件,但是在将该工程和资源文件打包为...
  • mm_bit
  • mm_bit
  • 2015-12-21 16:37
  • 9610

CSDN日报20170828——《4个方法快速打造你的阅读清单》

程序人生 | 4个方法快速打造你的阅读清单作者:foruok 下面提供四种方法,无论你是否经常读书,都可以使用它们快速构建起你的阅读清单: 从问题到图书 信息缺口 关联 跟...

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

Java注解@Retention&@Inherited@Target@IntDef@Documented

学习Java以来经常看到关于注解的使用,以前总是感觉自己用不到不太关注,总是需要用时上网找答案。 今天终于找个时间记录了下来。 从最简单的开始说:@IntDef枚举类型://定义: @IntDef...

ClassLoader和双亲委派机制

博文主要讲双亲委派机制和classload的模型、作用和使用,内容是作者学习java反射机制有关知识时记录的笔记。

Java垃圾回收器与内存分配策略

上一篇JVM内存模型讲述了Java虚拟机在运行时所管理的内存划分下的每个数据区域的各自用途,以及创建和销毁时间。当需要排查各种内存泄漏、内存溢出问题时,当来及收集成为系统达到更高并发量的瓶颈时,我们需...

Spring Cloud构建微服务架构(三)断路器

在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元间通过服务注册与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题...

当你编码时你在做什么:谈编程的本质(二)可爱的树

憋了好久的一篇,主题有点大一直没有写完,中间隔了很长时间现在已经有点捡不起来了,索性先发出来吧。至少个人觉得,完成的部分还是总结了一些有用的东西。关于Tree之上的属性、递归算法等,只能等状态回来了再...

OOM问题总结

OutOfMemoryError(OOM)就是常说的内存泄露问题。为了能够使Android应用程序能够高效快速地运行,所以Android的每个应用程序都会用一个专有的Davilk虚拟机(Android...

又一年对Android消息机制(Handler&Looper)的思考

前言Android消息机制对于每一个Android开发者来说都不陌生,在日常的开发中我们不可避免的要经常涉及这部分的内容。从开发角度来说,Handler是Android消息机制的上层接口,这使得在开发...

异常、堆内存溢出、OOM的几种情况

1堆内存溢出 2Java异常 OOM1、堆内存溢出【情况一】:   java.lang.OutOfMemoryError: Java heap space:这种是java堆内存不够,一个原因是真不够...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)