转载:详解Java 自动装箱与拆箱的实现原理
原文:http://www.jb51.net/article/111847.htm
什么是自动装箱和拆箱
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。原始类型byte, short, char, int, long, float, double 和 boolean 对应的封装类为Byte, Short, Character, Integer, Long, Float, Double, Boolean。
下面例子是自动装箱和拆箱带来的疑惑
public class Test {
public static void main(String[] args) {
test();
} public static void test() {
int i = 40;
int i0 = 40;
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
Double d1=1.0;
Double d2=1.0; System.out.println("i=i0\t" + (i == i0));
System.out.println("i1=i2\t" + (i1 == i2));
System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));
System.out.println("i4=i5\t" + (i4 == i5));
System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));
System.out.println("d1=d2\t" + (d1==d2)); System.out.println();
}
}
请看下面的输出结果跟你预期的一样吗?
输出的结果:
i=i0 true
i1=i2 true
i1=i2+i3 true
i4=i5 false
i4=i5+i6 true
d1=d2 false
为什么会这样?带着疑问继续往下看。
自动装箱和拆箱的原理
自动装箱时编译器调用valueOf将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。
明白自动装箱和拆箱的原理后,我们带着上面的疑问进行分析下Integer的自动装箱的实现源码。如下:
public static Integer valueOf(int i) {
//判断i是否在-128和127之间,如果不在此范围,则从IntegerCache中获取包装类的实例。否则new一个新实例
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
} //使用亨元模式,来减少对象的创建(亨元设计模式大家有必要了解一下,我认为是最简单的设计模式,也许大家经常在项目中使用,不知道他的名字而已)
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; //静态方法,类加载的时候进行初始化cache[],静态变量存放在常量池中
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h; cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
} private IntegerCache() {}
}
Integer i1 = 40; 自动装箱,相当于调用了Integer.valueOf(40);方法。
首先判断i值是否在-128和127之间,如果在-128和127之间则直接从IntegerCache.cache缓存中获取指定数字的包装类;不存在则new出一个新的包装类。
IntegerCache内部实现了一个Integer的静态常量数组,在类加载的时候,执行static静态块进行初始化-128到127之间的Integer对象,存放到cache数组中。cache属于常量,存放在java的方法区中。
接着看下面是java8种基本类型的自动装箱代码实现。如下:
//boolean原生类型自动装箱成Boolean
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
} //byte原生类型自动装箱成Byte
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
} //byte原生类型自动装箱成Byte
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
} //char原生类型自动装箱成Character
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
} //int原生类型自动装箱成Integer
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
} //int原生类型自动装箱成Long
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);
} //double原生类型自动装箱成Double
public static Double valueOf(double d) {
return new Double(d);
} //float原生类型自动装箱成Float
public static Float valueOf(float f) {
return new Float(f);
}
通过分析源码发现,只有double和float的自动装箱代码没有使用缓存,每次都是new 新的对象,其它的6种基本类型都使用了缓存策略。
使用缓存策略是因为,缓存的这些对象都是经常使用到的(如字符、-128至127之间的数字),防止每次自动装箱都创建一此对象的实例。
而double、float是浮点型的,没有特别的热的(经常使用到的)数据的,缓存效果没有其它几种类型使用效率高。
下面在看下装箱和拆箱问题解惑。
//1、这个没解释的就是true
System.out.println("i=i0\t" + (i == i0)); //true
//2、int值只要在-128和127之间的自动装箱对象都从缓存中获取的,所以为true
System.out.println("i1=i2\t" + (i1 == i2)); //true
//3、涉及到数字的计算,就必须先拆箱成int再做加法运算,所以不管他们的值是否在-128和127之间,只要数字一样就为true
System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));//true
//比较的是对象内存地址,所以为false
System.out.println("i4=i5\t" + (i4 == i5)); //false
//5、同第3条解释,拆箱做加法运算,对比的是数字,所以为true
System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));//true
//double的装箱操作没有使用缓存,每次都是new Double,所以false
System.out.println("d1=d2\t" + (d1==d2));//false
相信你看到这就应该能明白上面的程序输出的结果为什么是true,false了,只要掌握原理,类似的问题就迎刃而解了,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
转载:详解Java 自动装箱与拆箱的实现原理的更多相关文章
- 【转】java 自动装箱与拆箱
java 自动装箱与拆箱 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好的记忆,毅然决定还是用一篇博客来代替我的记忆: java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的 ...
- Java进阶(三十七)java 自动装箱与拆箱
Java进阶(三十七)java 自动装箱与拆箱 前言 这个是jdk1.5以后才引入的新的内容.java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装称为装箱,解包装称为 ...
- Java 自动装箱与拆箱
Java 自动装箱与拆箱(Autoboxing and unboxing) 什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供 ...
- JAVA基础之——三大特征、接口和抽象类区别、重载和重写区别、==和equals区别、JAVA自动装箱和拆箱
1 java三大特征 1)封装:即class,把一类实体定义成类,该类有变量和方法. 2)继承:从已有的父类中派生出子类,子类实现父类的抽象方法. 3)多态:通过父类对象可以引用不同的子类,从而实现不 ...
- [转]java 自动装箱与拆箱
转自:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2451996.html 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好 ...
- [转]JAVA自动装箱和拆箱
http://www.cnblogs.com/dolphin0520/p/3780005.html 1.Java数据类型 装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类 ...
- 全面理解java自动装箱和拆箱(转)
自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...
- Java——Java自动装箱和拆箱
一.什么是自动装箱和拆箱: 我们知道java为8种基本类型分别提供了对应的包装类型,在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i=new I ...
- Java自动装箱和拆箱
jdk5.0之后,在基本数据类型封装类之间增加了自动装箱和拆箱的功能,其实“自动”的实现很简单,只是将装箱和拆箱通过编译器,进行了“自动补全”,省去了开发者的手动操作. 而进行封装类与对应基本数据类型 ...
随机推荐
- 【模板】Splay
Splay 均摊复杂度证明见此处 \(\rightarrow\) 链接 代码如下 #include <bits/stdc++.h> using namespace std; const i ...
- 【POJ3585】Accumulation Degree 二次扫描与换根法
简单来说,这是一道树形结构上的最大流问题. 朴素的解法是可以以每个节点为源点,单独进行一次dp,时间复杂度是\(O(n^2)\) 但是在朴素求解的过程中,相当于每次都求解了一次整棵树的信息,会做了不少 ...
- [hihocoder1509][异或排序]
hihocoder1509 思路 对于每两个数,从二进制的高位到低位考虑,发现,若前面一个的当前位是1,后面一个的当前位置是0,那么s的当前位置必须是1.反之,若前面是0,后面是1,那么s的当前位置必 ...
- [poj3046][Ant counting数蚂蚁]
题目链接 http://noi.openjudge.cn/ch0206/9289/ 描述 Bessie was poking around the ant hill one day watching ...
- centos7部署posgresql和kong总结
之前在macos系统测试安装psql和kong,但是实际环境中,大部分是部署在linux服务器上.下面记录了在centos7上部署postgresql和kong的总结以及遇到的一些问题的解决. 查看c ...
- 动态分配内存 new
a=]; ;i<=n;i++) a[i]=]; 感觉比malloc好用. 动态初始化后,值并非全为0,注意!
- 求n(n>=2)以内的质数/判断一个数是否质数——方法+细节优化
#include <stdio.h> #include <stdlib.h> //判断i是否质数,需要判断i能否被(long)sqrt(i)以内的数整除 //若i能被其中一个质 ...
- DOM表格操作
注意:就算代码中不包含<tbody>标签,浏览器解析时也可能会自动添加,因此需要注意子元素的选择 表格操作用到的属性: 1.tHead 2.tBodies 3.tFoot 更为细致的有: ...
- ip更换
Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...
- 跨iOS SDK版本编译问题
iOS开发时会考虑两种SDK版本兼容性:一个是运行时SDK版本的兼容,即已构建/已发布的APP能在不同系统版本的用户手机上正常运行:一个是编译时SDK版本的兼容,即使用不同版本的SDK编译项目都能正常 ...