什么是自动装箱,拆箱
先抛出定义,Java中基础数据类型与它们的包装类进行运算时,编译器会自动帮我们进行转换,转换过程对程序员是透明的,这就是装箱和拆箱,装箱和拆箱可以让我们的代码更简洁易懂
耗时问题
在说 Java 的自动装箱和自动拆箱之前,我们先看一个例子。
这个错误我在项目中犯过(尴尬),拿出来共勉!
private static long getCounterResult() { Long sum = 0L; final int length = Integer.MAX_VALUE; for (int i = 0; i < length; i++) { sum += i; } return sum; } public static void main(String[] args) { long startCountTime = System.currentTimeMillis(); long result = getCounterResult(); long endCountTime = System.currentTimeMillis(); System.out.println("result = " + result + ", and take up time : " + (endCountTime - startCountTime) / 1000 + "s"); }
在我的电脑(macOS 64位系统,配置较高),打印结果如下:
result = 2305843005992468481, and take up time : 12s
居然使用了 12s,是可忍叔不可忍,再正常不过的代码怎么会耗时这么久呢?如果在配置差一点的电脑上运行耗时会更久(惊呆了.jpg)。
我们不妨先阅读下面的内容,再来分析、解决上述耗时的问题。
基本概念
自从 jdk1.5 之后就有了自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)。
自动装箱,就是 Java 自动将原始(基本)类型转换成对应的封装器(对象)类型的过程,比如将 int 的变量转换成 Integer 对象,这个过程叫做装箱。
自动拆箱,就是 Java 自动将封装器(对象)类型转换成基本类型的过程,如将 Integer 对象转换成 int 类型值,这个过程叫做拆箱。
之所以称之为自动装箱和拆箱,是因为这些操作并非人工(程序猿)操作的,而是 Java 自带的一个特性。
下表是 Java 中的基本类型和对应的封装类型的对应表:
基本类型 | 封装器类 |
---|---|
int | Integer |
byte | Byte |
long | Long |
float | float |
double | Double |
char | Character |
boolean | Boolean |
自动装箱示例:
int a = 3; Integer b = a;
自动拆箱示例:
Integer b = new Integer(7); int a = b;
Integer/int 自动拆箱和装箱
下面这段代码是 Integer 的源码中 valueOf 方法。
/** * 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) { // 如果i的值大于-128小于127则返回一个缓冲区中的一个Integer对象 if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; // 否则返回 new 一个Integer 对象 return new Integer(i); }
我们在执行下面的这句代码,如下:
Integer i = 100;
上面的代码等同于下面的代码:
Integer i = Integer.valueOf(100);
结合上面的源码可以看出来,如果数值在 [-128,127] 之间(双闭区间),不会重新创建 Integer 对象,而是从缓存中(常量池)直接获取,从常量池中获取而不是堆栈操作,读取数据要快很多。
我们再来看一下常见的基础面试题(请给出打印结果),如下:
public static void main(String[] args) { // ⓵ Integer a = new Integer(121); Integer b = new Integer(121); System.out.println(a == b); // ⓶ Integer c = 121; Integer d = 121; System.out.println(c == d); // ⓷ Integer e = 129; Integer f = 129; System.out.println(e == f); // ⓸ int g = 50; Integer h = new Integer(50); System.out.println(g == h); }
分析结果:
⓵: false, 两个对象进行比较分别指向了不同堆内存
⓶: true, 自动装箱且数值在 [-128,127] 之间(双闭区间)
⓷: false, 自动装箱且数值不在 [-128,127] 之间(双闭区间)
⓸: true, 自动拆箱且数值在 [-128,127] 之间(双闭区间)
解析耗时问题
类 Long 对应的也有一个 valueof 方法,源码如下:
public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); }
这个和 Integer 的很像,道理上面说过,这里不再赘述。
在开篇的例子中,getCounterResult 方法有下面这句代码,如下:
Long sum = 0L;
很明显我们声明了一个 Long 的对象 sum,由于自动装箱,这句代码并没有语法上面的错误,编译器当然也不会报错。上面代码等同于如下代码:
Long sum = Long.valueof(0);
在 for 循环中,超过 [-128,127] 就会创建新的对象,这样不断的创建对象,不停的申请堆内存,程序执行自然也就比较耗时了。
修改一下代码,如下:
private static long getCounterResult() { // 修改为普通的基本类型数据 long sum = 0L; final int length = Integer.MAX_VALUE; for (int i = 0; i < length; i++) { sum += i; } return sum; } public static void main(String[] args) { long startCountTime = System.currentTimeMillis(); long result = getCounterResult(); long endCountTime = System.currentTimeMillis(); System.out.println("result = " + result + ", and take up time : " + (endCountTime - startCountTime) / 1000 + "s"); }
执行时间大大缩短。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对小牛知识库的支持。
本文向大家介绍自动装箱和拆箱?相关面试题,主要包含被问及自动装箱和拆箱?时的应答技巧和注意事项,需要的朋友参考一下 自动装箱是Java 编译器在基本数据类型和对应的对象包装类型之间做的一个转化。 比如:把int转化成 Integer,double转化成 Double,等等。反之就是自动拆箱。 原始类型: boolean,char,byte,short,int,long,float,double
问题内容: 从JDK 5.0开始,自动装箱/拆箱是在Java中引入的,这种技巧很简单而且很有帮助,但是当我开始测试包装器类和原始类型之间的不同转换时,我真的很困惑自动装箱的概念在Java中的工作原理: Boxing 尝试不同的情况下后(),这是由编译器所接受的唯一情况是,当值的上做作运算符右侧的类型是int。当我查看源代码时,发现它仅实现一个带参数的构造函数。 因此,我的结论是,自动装箱的概念基于
问题内容: 为什么第二段代码更快? 问题答案: 自动装箱使用,内部将Integer对象缓存为小整数(默认情况下为-128至127,但是最大值可以使用“ java.lang.Integer.IntegerCache.high”属性进行配置-请参见Integer.valueOf的源代码) ,因此与直接调用不同。因为在调用之前可以快速检查整数值的大小,所以直接调用要快一些(尽管如果您有很多小整数,它会使
本文向大家介绍C#装箱和拆箱原理详解,包括了C#装箱和拆箱原理详解的使用技巧和注意事项,需要的朋友参考一下 .NET包含一个特殊的Object类,可以接受任意的数据类型的值,当所传递或所赋值的类型不是一个特定的数据类型时,object类就提供了一种传递参数和赋值的通用方法。赋给object的值必须作为引用类型,并存放砸托管堆中。 装箱: int age = 24; object refAge= a
主要内容:装箱和拆箱,包装类的应用在 Java 的设计中提倡一种思想,即一切皆对象。但是从数据类型的划分中,我们知道 Java 中的数据类型分为基本数据类型和引用数据类型,但是基本数据类型怎么能够称为对象呢?于是 Java 为每种基本数据类型分别设计了对应的类,称之为 包装类(Wrapper Classes),也有地方称为外覆类或数据类型类。 包装类和基本数据类型的关系如下表所示。 基本数据类型及对应的包装类 序号 基本数据类型
问题内容: 以下代码进行编译(使用Java 8): 但是它是做什么的呢? 取消装箱: 或盒子: 那么它是比较两个对象(按引用)还是按值比较两个变量? 请注意,对于某些数字,引用比较将产生正确的结果,因为Integer类会维护一个介于to 之间的值的内部缓存(另请参见TheLostMind的注释)。这就是我在示例中使用的原因,也是为什么我特别询问拆箱/装箱而不是比较结果的原因。 问题答案: 它在JL