zoukankan      html  css  js  c++  java
  • [转]JAVA自动装箱和拆箱

    http://www.cnblogs.com/dolphin0520/p/3780005.html 

    1.Java数据类型

    装箱和拆箱之前,我们先来了解一下Java的基本数据类型。

          在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference Type(引用类型)。基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。所以Java提供了针对每种基本类型的包装类型。如下:

     

    Java基本数据类型
    INDEX
    基本类型
     大小
    数值范围
    默认值
    包装类型
    1 boolean
       --- true,false
    false
    Boolean
    2 byte
    8bit -2^7 -- 2^7-1
    0 Byte
    3 char
    16bit
    uffff
    u0000
    Character
    4 short
    16bit
    -2^15 -- 2^15-1
    0 Short
    5 int 
    32bit -2^31 -- 2^31-1
    0 Integer
    6 long
    64bit -2^63 -- 2^63-1
    0 Long
    7 float 
    32bit
    IEEE 754
    0.0f Float
    8 double 
    64bit
    IEEE 754
    0.0d Double
    9 void
        ---     --- --- Void

    Java 1.5中引入了自动装箱和拆箱机制:

      (1)把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()hashCode()getClass()equals()等方法。 如下:

          Integer a=3;//这是自动装箱

            其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(int i)返回一个表示指定int值的Integer对象,那么就变成这样: 

              Integer a=3;   =>    Integer a=Integer.valueOf(3);

       (2)的方向相反,将IntegerDouble这样的引用类型的对象重新简化为基本类型的数据。

             如下://这是拆箱

    编译器内部会调用int intValue()返回该Integer对象的int值

             代码如下:  

    1. Integer integer1 = 100;  
    2. Integer integer2 = 100;  
    3. System.out.println("integer1==integer2: " + (integer1 == integer2));// true  自动装箱的两个缓存中的 Integer对象的引用比较  
    4. System.out.println("integer1.equals(integer2): " + (integer1.equals(integer2)));// true  
    5. System.out.println("integer1.compare(integer2): " + integer1.compareTo(integer2));// 0      
    6. Integer integer3 = 200;  
    7. Integer integer4 = 200;  
    8. System.out.println("integer3==integer4: " + (integer3 == integer4));// false 自动装箱的两个new Integer的引用比较  
    9. System.out.println("integer3>integer4: " + (integer3 > integer4)); // false 将两个对象拆箱,再比较大小  
    10. System.out.println("integer3.equals(integer4): " + (integer3.equals(integer4)));// true  
    11. System.out.println("integer3.compare(integer4): " + integer3.compareTo(integer4));// 0     
    12. Integer integer5 = new Integer(100);  
    13. Integer integer6 = new Integer(100);  
    14. System.out.println("integer5==integer6: " + (integer5 == integer6)); // false 两个不同的Integer对象引用的比较  
    15. System.out.println("integer5.equals(integer6): " + (integer5.equals(integer6)));// true  
    16. System.out.println("integer5.compare(integer6): " + integer5.compareTo(integer6));// 0      
    17. int int1 = 100;  
    18. System.out.println("integer1==int1: " + (integer1 == int1));// true  Integer缓存对象拆箱后与int比较  
    19. System.out.println("integer1.equals(int1): " + (integer1.equals(int1)));// true  
    20. System.out.println("integer1.compare(int1): " + integer1.compareTo(int1));// 0        
    21. int int2 = 200;  
    22. System.out.println("integer3==int2: " + (integer3 == int2));// true  Integer对象拆箱后与int比较  
    23. System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true  
    24. System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0   

              反编译一下看得更清楚:   

     

    1. Integer localInteger1 = Integer.valueOf(100);  
    2. Integer localInteger2 = Integer.valueOf(100);  
    3. System.out.println(new StringBuilder().append("integer1==integer2: ").append(localInteger1 == localInteger2).toString());  
    4. System.out.println(new StringBuilder().append("integer1.equals(integer2): ").append(localInteger1.equals(localInteger2)).toString());  
    5. System.out.println(new StringBuilder().append("integer1.compare(integer2): ").append(localInteger1.compareTo(localInteger2)).toString());  
    6. Integer localInteger3 = Integer.valueOf(200);  
    7. Integer localInteger4 = Integer.valueOf(200);  
    8. System.out.println(new StringBuilder().append("integer3==integer4: ").append(localInteger3 == localInteger4).toString());  
    9. System.out.println(new StringBuilder().append("integer3>integer4: ").append(localInteger3.intValue() > localInteger4.intValue()).toString());  
    10. System.out.println(new StringBuilder().append("integer3.equals(integer4): ").append(localInteger3.equals(localInteger4)).toString());  
    11. System.out.println(new StringBuilder().append("integer3.compare(integer4): ").append(localInteger3.compareTo(localInteger4)).toString());  
    12. Integer localInteger5 = new Integer(100);  
    13. Integer localInteger6 = new Integer(100);  
    14. System.out.println(new StringBuilder().append("integer5==integer6: ").append(localInteger5 == localInteger6).toString());  
    15. System.out.println(new StringBuilder().append("integer5.equals(integer6): ").append(localInteger5.equals(localInteger6)).toString());  
    16. System.out.println(new StringBuilder().append("integer5.compare(integer6): ").append(localInteger5.compareTo(localInteger6)).toString());  
    17. int i = 100;  
    18. System.out.println(new StringBuilder().append("integer1==int1: ").append(localInteger1.intValue() == i).toString());  
    19. System.out.println(new StringBuilder().append("integer1.equals(int1): ").append(localInteger1.equals(Integer.valueOf(i))).toString());  
    20. System.out.println(new StringBuilder().append("integer1.compare(int1): ").append(localInteger1.compareTo(Integer.valueOf(i))).toString());  
    21. int j = 200;  
    22. System.out.println(new StringBuilder().append("integer3==int2: ").append(localInteger3.intValue() == j).toString());  
    23. System.out.println(new StringBuilder().append("integer3.equals(int2): ").append(localInteger3.equals(Integer.valueOf(j))).toString());  
    24. System.out.println(new StringBuilder().append("integer3.compare(int2): ").append(localInteger3.compareTo(Integer.valueOf(j))).toString());  

    4.源码分析

    4.1 valueOf工厂方法

     

    1. public static Integer valueOf(inti) {    
    2.     if(i >= -128 &&i <=IntegerCache.high)    
    3.        //如果i在-128~high之间,就直接在缓存中取出i的Integer类型对象  
    4.        return IntegerCache.cache[i + 128];    
    5.     else  
    6.        return new Integer(i); //否则就在堆内存中创建   
    7. }    

            4.2 IntegerCache内部类

            下面我们看看Integer源码中是怎么在内部缓存数值的。   

    1. private static class IntegerCache {//内部类,注意它的属性都是定义为static final  
    2.     static final inthigh; //缓存上界  
    3.     static final Integer cache[];//cache缓存是一个存放Integer类型的数组  
    4.   
    5.     static {//静态语句块  
    6.         final int low = -128;//缓存下界,值不可变  
    7.   
    8.         // high value may beconfigured by property  
    9.         int h = 127;// h值,可以通过设置jdk的AutoBoxCacheMax参数调整(参见(3))  
    10.         if (integerCacheHighPropValue !=null) {  
    11.             // Use Long.decode here to avoid invoking methods that  
    12.             // require Integer's autoboxing cache to be initialized  
    13.             // 通过解码integerCacheHighPropValue,而得到一个候选的上界值  
    14.             int i = Long.decode(integerCacheHighPropValue).intValue();  
    15.             // 取较大的作为上界,但又不能大于Integer的边界MAX_VALUE  
    16.             i = Math.max(i, 127);//上界最小为127  
    17.             // Maximum array size is Integer.MAX_VALUE  
    18.             h = Math.min(i, Integer.MAX_VALUE - -low);  
    19.         }  
    20.         high = h; //上界确定,此时high默认一般是127  
    21.         // 创建缓存块,注意缓存数组大小  
    22.         cache =new Integer[(high - low) + 1];  
    23.         int j = low;  
    24.         for(int k = 0; k <cache.length; k++)  
    25.             cache[k] =new Integer(j++);// -128到high值逐一分配到缓存数组  
    26.     }  
    27.   
    28.     private IntegerCache() {}//构造方法,不需要构造什么  
    29. }  

       4.3 用integerCacheHighPropValue变量设置自动装箱池大小

       /**

         * Cache to support theobject identity semantics of autoboxing for values between

         * -128 and 127(inclusive) as required by JLS.

         *

         * The cache isinitialized on first usage. During VM initialization the

         * getAndRemoveCachePropertiesmethod may be used to get and remove any system

         * properites thatconfigure the cache size. At this time, the size of the

         * cache may be controlledby the vm option -XX:AutoBoxCacheMax=<size>.

         */

            

     

    1. // value of java.lang.Integer.IntegerCache.high property(obtained during VMinit)  
    2. private static String integerCacheHighPropValue;  
    3.   
    4. static void getAndRemoveCacheProperties() {  
    5.     if (!sun.misc.VM.isBooted()) {  
    6.         Properties props= System.getProperties();  
    7.         integerCacheHighPropValue=  
    8.             (String)props.remove("java.lang.Integer.IntegerCache.high");  
    9.         if (integerCacheHighPropValue!=null)  
    10.             System.setProperties(props); // remove from system props  
    11.      }  
    12.  }  

          4.4 设置-XX:AutoBoxCacheMax=<size>选项再次测试<3>中代码

             在eclipse中,选中源文件,右键Run as—>RunConfiguratio--->Arguments,在VM arguments中做以下设置:

             

             运行,控制台报错:Unrecognized VM option 'AutoBoxCacheMax=256'

             网上找到http://rednaxelafx.iteye.com/blog/680746这篇文章,里面解释的很清楚,使用Oracle/SunJDK 6,在server模式下,使用-XX:AutoBoxCacheMax=NNN参数即可将Integer的自动缓存区间设置为[-128,NNN]。这个参数是server模式专有的。而我使用的是HotSpot Client VM,那怎么切换成server模式呢?网上找到http://www.iteye.com/topic/857587这篇文章也很好的解决了我的问题,网上牛人好多啊,学习了。

             虚拟机版本与模式查看:
             java -version //查看JVM默认的环境
             java -client -version //查看JVM的客户端环境,针对GUI优化,启动速度快,运行速度不如server
             java -server -version //查看JVM的服务器端环境,针对生产环境优化,运行速度快,启动速度慢

             我的虚拟机默认版本如下:

            

             需要切换到

            

            切换方法如下:

            找到JAVA_HOME/jre/lib/i386/jvm.cfg,这就是JVM默认的查找顺序,内容如下
            -client KNOWN
            -server KNOWN
            -hotspot ALIASED_TO -client
            -classic WARN
            -native ERROR
            -green ERROR
           只需要把-server和-clent换个位置就行了.如下
           -server KNOWN
           -client KNOWN
           -hotspot ALIASED_TO -client
           -classic WARN
           -native ERROR
           -green ERROR

           最后运行,得到如下结果:

           Integer integer1 = 100;

           Integer integer2 = 100;

           System.out.println("integer1==integer2: " + (integer1 == integer2));// true 自动装箱的两个缓存中的 Integer对象的引用比较

           System.out.println("integer1.equals(integer2): " + (integer1.equals(integer2)));// true

           System.out.println("integer1.compare(integer2): " + integer1.compareTo(integer2));// 0      

           Integer integer3 = 200;

           Integer integer4 = 200;

           System.out.println("integer3==integer4: " + (integer3 ==integer4)); // true 自动装箱的两个new Integer的引用比较

           System.out.println("integer3>integer4: " + (integer3 > integer4)); // false将两个对象拆箱,再比较大小

           System.out.println("integer3.equals(integer4): " + (integer3.equals(integer4)));// true

           System.out.println("integer3.compare(integer4): " + integer3.compareTo(integer4));// 0  

           Integer integer5 =new Integer(100);

           Integer integer6 =new Integer(100);

           System.out.println("integer5==integer6: " + (integer5 == integer6));// false两个不同的Integer对象引用的比较

           System.out.println("integer5.equals(integer6): " + (integer5.equals(integer6)));// true

           System.out.println("integer5.compare(integer6): " + integer5.compareTo(integer6));// 0   

           intint1 = 100;

           System.out.println("integer1==int1: " + (integer1 == int1));// true Integer缓存对象拆箱后与int比较

           System.out.println("integer1.equals(int1): " + (integer1.equals(int1)));// true

           System.out.println("integer1.compare(int1): " + integer1.compareTo(int1));// 0      

           intint2 = 200;

           System.out.println("integer3==int2: " + (integer3 == int2));// true Integer对象拆箱后与int比较

           System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true

           System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0

           可以看到黄色那行和3的测试结果相比,结果变成了true,说明-XX:AutoBoxCacheMax=<size>这个选项真的可以改变Integer的缓存大小。  

      在很多很是时候会遇到“==”问题,这时候也是与自动拆装箱是联系的,具体原则是:当 “==”运算符的两个操作数都是 包装器类型的引用时,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。用 下面代码验证一下:

            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 364;
            Integer f = 364;
            Long g = 3L;
            Long h = 2L;
            System.out.println(c==d);//true
            System.out.println(e==f);//false
            System.out.println(c==(a+b));//true
            System.out.println(c.equals(a+b));//true
            System.out.println(g==(a+b));//true
            System.out.println(g.equals(a+b));//false
            System.out.println(g.equals(a+h));//true
    

      c.equals(a+b) 会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b会先各自调用intValue方法,得到了加法运算后的数值之后,便调用 Integer.valueOf方法,再进行equals比较。注意一下:g.equals(a+h),先拆箱是a与h相加后装箱后的调用的是 Long.valueof方法。这个过程是先拆箱后装箱的操作顺序。

    5.疑问解答

           5.1 看IntegerCache的源码可以知道Integer的缓存至少要覆盖[-128, 127]的范围,为什么?

              参见《The Java™Language Specification Java SE 7Edition 5.1.7》Boxing Conversion的描述

              If the valuepbeing boxed is true, false, a byte, or a char inthe range u0000 to u007f,or an int or short numberbetween -128 and 127 (inclusive),then letr1andr2be the results of any two boxing conversions ofp. It is always the case thatr1==r2.

            5.2 其它基本数据类型对应的包装类型的自动装箱池大小

              Byte,Short,Long对应的是-128~127

              Character对应的是0~127

              Float和Double没有自动装箱池

    6.总结     

              Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率。通过上面的研究和测试,结论如下:

            (1)Integer int之间可以进行各种比较;Integer对象将自动拆箱后与int值比较

            (2)两个Integer对象之间也可以用><等符号比较大小;两个Integer对象都拆箱后,再比较大小

            (3) 两个Integer对象最好不要用==比较。因为:-128~127范围(一般是这个范围)内是取缓存内对象用,所以相等,该范围外是两个不同对象引用比较,所以不等。

             以上是我个人学习和研究结论,如有不正确之处,欢迎大家指正。

  • 相关阅读:
    随意谈谈tcp
    %matplotlib inline 被注释掉后,pycharm不能生成图
    (转)SQL Server 2012 手动安装帮助文档+排错
    (转)线性回归数学推导(详细过程)
    numpy的函数使用
    SQL 测验题目(30道)
    SourceInsight 支持 Python
    (转)GitHub上想下载单个文件方法
    (转)SQL Server 数据类型映射
    数据库的基本使用(C#语言)
  • 原文地址:https://www.cnblogs.com/wxgblogs/p/5799596.html
Copyright © 2011-2022 走看看