原文: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 自动装箱与拆箱的实现原理的更多相关文章

  1. 【转】java 自动装箱与拆箱

    java 自动装箱与拆箱 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好的记忆,毅然决定还是用一篇博客来代替我的记忆: java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的 ...

  2. Java进阶(三十七)java 自动装箱与拆箱

    Java进阶(三十七)java 自动装箱与拆箱 前言 这个是jdk1.5以后才引入的新的内容.java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装称为装箱,解包装称为 ...

  3. Java 自动装箱与拆箱

    Java 自动装箱与拆箱(Autoboxing and unboxing)   什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供 ...

  4. JAVA基础之——三大特征、接口和抽象类区别、重载和重写区别、==和equals区别、JAVA自动装箱和拆箱

    1 java三大特征 1)封装:即class,把一类实体定义成类,该类有变量和方法. 2)继承:从已有的父类中派生出子类,子类实现父类的抽象方法. 3)多态:通过父类对象可以引用不同的子类,从而实现不 ...

  5. [转]java 自动装箱与拆箱

    转自:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2451996.html 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好 ...

  6. [转]JAVA自动装箱和拆箱

    http://www.cnblogs.com/dolphin0520/p/3780005.html 1.Java数据类型 装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类 ...

  7. 全面理解java自动装箱和拆箱(转)

    自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...

  8. Java——Java自动装箱和拆箱

    一.什么是自动装箱和拆箱: 我们知道java为8种基本类型分别提供了对应的包装类型,在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i=new I ...

  9. Java自动装箱和拆箱

    jdk5.0之后,在基本数据类型封装类之间增加了自动装箱和拆箱的功能,其实“自动”的实现很简单,只是将装箱和拆箱通过编译器,进行了“自动补全”,省去了开发者的手动操作. 而进行封装类与对应基本数据类型 ...

随机推荐

  1. 【模板】倍增+Floyd

    题目大意:给定一个 N 个顶点的邻接矩阵.起点顶点.终点顶点,求至少经过 K 条边(边可以重复)从起点到终点的最短路长度,若不能到达,输出 -1. 题解:至少经过 K 条边和恰好经过 K 条边的初始条 ...

  2. react中实现点击跳转到新页面方法

    实现点击跳转到新页面,可以有两种形式,一个是本地页面打开,一个是本地页面不变跳转到新的页面. (一)页面点击本地页面打开新页面 引入ant的Button组件 <Button style={{ba ...

  3. DTLS协议中client/server的认证过程和密钥协商过程

    我的总结:DTLS的握手就是协商出一个对称加密的秘钥(每个客户端的秘钥都会不一样),之后的通信就要这个秘钥进行加密通信.协商的过程要么使用非对称加密算法进行签名校验身份,要么通过客户端和服务器各自存对 ...

  4. spring校验注解

    @Null   被注释的元素必须为 null @NotNull    被注释的元素必须不为 null @AssertTrue     被注释的元素必须为 true @AssertFalse    被注 ...

  5. Linux集群部署自定义时间同步服务器(ntpd)

    Linux集群部署自定义时间同步服务器(ntpd) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 身为一名运维人员,在搭建集群的时候,第一步需要做的就是同步每个机器的时间,尤其是在 ...

  6. Nginx 入门指南

    Nginx 入门指南 简介: Nginx 是一款轻量级的 Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,其特点是占有内存少,并发能力强.本教程根据淘宝核心系统服务器平台组的 ...

  7. 学习windows编程 day3 之窗口绘画一:点线绘制

    #include <windows.h> #include <math.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT message, ...

  8. Spark记录-Scala基础程序实例

    object learn { def main(args:Array[String]):Unit={ println("请输入两个数字:") var a:Int=Console.r ...

  9. jdk8中奖Date转换为String格式的方法

    public static String getLocalDateStr(Date date,String formatter) { DateTimeFormatter dateTimeFormatt ...

  10. 微信现金红包 python

    微信现金红包发送接口,好像没法限制一个用户一个活动只能领取一次红包,在调用红包接口上,自己做了限制 REDPACK_RECORD 建表sql -- Create table create table ...