转载:详解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了,只要掌握原理,类似的问题就迎刃而解了,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

原文地址:https://www.cnblogs.com/huiy/p/8470546.html

时间: 2024-10-29 04:08:24

转载:详解Java 自动装箱与拆箱的实现原理的相关文章

【转】java 自动装箱与拆箱

java 自动装箱与拆箱 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好的记忆,毅然决定还是用一篇博客来代替我的记忆: java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装成为装箱,解包装称为拆箱): 其实按照我自己的理解自动装箱就可以简单的理解为将基本数据类型封装为对象类型,来符合java的面向对象:例如用int来举例: //声明一个Integer对象 Integer num = 10; //以上的声明就是用到了自动的装箱:解析为 Integer

java自动装箱与拆箱

基本数据(Primitive)类型的自动装箱(autoboxing).拆箱(unboxing)是J2SE 5.0提供的新功能,跟泛型.变长参数等一样,这也是一颗"语法糖",之前介绍jvm早期优化的时候说过,语法糖对运行期的代码没有任何影响,其目的仅仅是方便程序员使用,所有语法糖编译后都将会还原成基础语法.这次介绍的自动装箱和拆箱也不例外.看下头的例子: public class Demo { public static void main(String[] args) { Intege

Java 自动装箱和拆箱

JDK1.5之后的功能 自动装箱:指的是开发人员可以把一个基本数据类型直接赋给对应的包装类 自动拆箱:指开发人员可以把一个包装类对象直接赋给对应的基本数据类型 public static void main(String[] args) { Integer i = 1; //装箱 int j = i ; //拆箱 } public static void main(String[] args) { List list = new ArrayList(); list.add(1); int j=

跟王老师学泛型(二):Java自动装箱与拆箱

Java 自动装箱与拆箱(Autoboxing and unboxing) 主讲教师:王少华 QQ群:483773664 学习目标: 掌握Java 基本数据对应的包装类 掌握Java 自动装箱与拆箱 一.基本数据类型对应的包装类 Java为每一种基本数据类型都提供了一个包装类,而这些包装类就在java.lang包中.Java共有8个包装类.如下图所示: 包装所有数字类型类都继承自Number类,Number类是一个抽象类. 二.什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).

java 自动装箱、拆箱

# java 自动装箱.拆箱 从 jdk 1.5 版本开始, 引入该功能. 一.自动装箱 将基本数据类型自动封装为对应封装类. 代码示例, Integer i = 100; 100属于基本类型int,会自动装箱,如下: Integer i = Integer.valueOf(100); 相当于, Integer i = new Integer(100); 二.自动拆箱 将封装类自动转换为对应的基本数据类型. 代码示例, Integer i = new Integer(100); int j =

[转]java 自动装箱与拆箱

转自:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2451996.html 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好的记忆,毅然决定还是用一篇博客来代替我的记忆: java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装成为装箱,解包装称为拆箱): 其实按照我自己的理解自动装箱就可以简单的理解为将基本数据类型封装为对象类型,来符合java的面向对象:例如用int来举例: //声明一个

Java 自动装箱与拆箱

Java为每种基本数据类型都提供了对应的对象类型.在Java SE5之前,如果要生成一个数值为7的Integer对象,代码示例:Integer i = new Integer(7);:Java SE5之后,Java提供了新的语法,简化了基本数据类型对象的使用,我们称之为自动装箱(autoboxing)与拆箱(unboxing).之前的代码可以简化为Integer i = 7;. Java提供的自动装箱盒拆箱,是在编译器层实现的.编译之后的字节码仍然是如Java SE5之前的形态.如自动装箱使用对

Java 自动装箱和拆箱那些事

1.JAVA的基本数据类型 在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference Type(引用类型).基本类型的数值不是对象,不能调用对象的toString().hashCode().getClass().equals()等方法.所以Java提供了针对每种基本类型的包装类型.如下: Java基本数据类型 INDEX 基本类型  大小 数值范围 默认值 包装类型 1 boolean    --- true,false false Boolean 2

[转]JAVA自动装箱和拆箱

1.Java数据类型 装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference Type(引用类型).基本类型的数值不是对象,不能调用对象的toString().hashCode().getClass().equals()等方法.所以Java提供了针对每种基本类型的包装类型.如下: Java基本数据类型 INDEX 基本类型  大小 数值范围 默认值 包装类型 1 boolean    --- t