zoukankan      html  css  js  c++  java
  • Java 中的装箱与拆箱

     

            当出现问题时,而有无法了解问题所在,看JDK源码和编译后的字节码;

    转载:http://blog.csdn.net/dreaming_my_dreams/article/details/8316085

    : 推荐博客

    J2SE5.0后推出了自动装箱和拆箱的功能,以提高我们的开发效率,然而自动装箱和拆箱实际上是通过编译器来支持的(并非语言本身,或者说虚拟机),因而这种支持也隐藏了部分内部实质,再加上某些类的优化(比如Integer里面的缓存等,参看关于缓存节),就更加容易在特定的环境下产生问题,并且如果不知道原来还无法调试。以下先是简单的介绍了编译器对装箱和拆箱的实现,并根据实现简单介绍一下可能会遇到的几个问题。

    装箱和拆箱实现

    以下装箱和拆箱代码:

     

           Object value = 10;
           
    int intValue = (Integer)value;
           Integer newIntValue 
    = new Integer(10);

    编译成字节码如下:

         0 bipush 10

         2 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [20]

         5 astore_1 [value]

         6 aload_1 [value]

         7 checkcast java.lang.Integer [21]

        10 invokevirtual java.lang.Integer.intValue() : int [26]

        13 istore_2 [intValue]

        14 new java.lang.Integer [21]

        17 dup

        18 bipush 10

        20 invokespecial java.lang.Integer(int) [30]

        23 astore_3 [newIntValue]

    从以上字节码可以看到10首先调用valueOf方法转换为Integer实例,再赋值该value,而value强制转换成Integer类后,会调用intValue方法,后赋值给intValue。这就是用编译器来实现装箱和拆箱。

     

    奇怪的NullPointerException

    查看以下代码:

     

           Integer value = null;
           
    int intValue = value;

    可以编译通过,但是运行的时候却会发生NullPointerException。这是由什么引起的呢?依然看一下字节码就可以了:

         0 aconst_null

         1 astore_1 [value]

         2 aload_1 [value]

        3 invokevirtual java.lang.Integer.intValue() : int [20]

         6 istore_2 [intValue]

    从字节码中可以看到,从value赋值该intValue事实上是直接在value实例上调用intValue函数。

    对当前代码,我们可以一眼就看出当前valuenull的问题,但是如果这个null是在很远以外的地方赋值的呢?或者是间接赋值呢?这个时候遇到这种问题就会比较诡异了。

     

    相等与不相等问题

    查看一下代码:

     

           Integer value1 = 100;
           Integer value2 
    = 100;
           System.out.println(
    "value1 == value2 is " + (value1 == value2));
          
           Integer value3 
    = 200;
           Integer value4 
    = 200;
           System.out.println(
    "value3 == value4 is " + (value3 == value4));

    这段代码会是什么结果?

    value1 == value2 is true

    value3 == value4 is false

     

    两段代码就是值不一样,其他的都一样,竟然会有区别?这个奥妙就因为装箱过程中调用的是valueOf方法,而valueOf方法对值在-128127之间的数值缓存了(参见关于缓存一节),因而value1value2的引用是相同的,而value3value4的引用是不一样的,而==比较的是引用,因而才会出现以上的结果。

    这确的做法应该是:

           Integer value1 = 100;
           Integer value2 
    = 100;
           System.out.println(
    "value1 == value2 is " + (value1.equals(value2)));
          
           Integer value3 
    = 200;
           Integer value4 
    = 200;

           System.out.println("value3 == value4 is " + (value3.equals(value4))); 

    这样的结果就是预料的结果了:

    value1 == value2 is true

    value3 == value4 is true

     

    所以我们要慎用“==”操作符。

     

    String中的相等与不等

    String中也有类似的情况,查看一下代码:

     

           String str1 = "abc";
           String str2 
    = "abc";
           System.out.println(
    "str1 == str2 is " + (str1 == str2));
          
           String str3 
    = new String("abc");
           String str4 
    = new String("abc");
           System.out.println(
    "str3 == str4 is " + (str3 == str4));

    执行结果:

    str1 == str2 is true

    str3 == str4 is false

     

    这是因为str1str2使用的是同一个字符串,即在字符常量中的字符串,而str3str4在使用字符常量中的字符为参数又创建出了两个新的字符串对象,因而在引用比较情况下是不等的。我们可以从字节码中得到这些信息(删除打印的代码):

         0 ldc <String "abc"> [20]

         2 astore_1 [str1]

         3 ldc <String "abc"> [20]

         5 astore_2 [str2]

         6 new java.lang.String [22]

         9 dup

        10 ldc <String "abc"> [20]

        12 invokespecial java.lang.String(java.lang.String) [24]

        15 astore_3 [str3]

        16 new java.lang.String [22]

        19 dup

        20 ldc <String "abc"> [20]

        22 invokespecial java.lang.String(java.lang.String) [24]

        25 astore 4 [str4]

    正确的做法还是调用equals方法,而不是使用“==”操作符。

     

    关于缓存

    据目前信息,有缓存的类有:ByteShortIntegerLong以及Boolean类。而这种缓存也只是在调用valueOf(静态)方法的时候才会存在(装箱正是调用了valueOf方法)。对整型,缓存的值都是-128127(包括-128127)之间,其他值都不缓存,而对Boolean类型只有truefalse值。代码如下:

    public final class Integer extends Number {
        
    public static Integer valueOf(int i) {
            
    if(i >= -128 && i <= IntegerCache.high)
                
    return IntegerCache.cache[i + 128];
            
    else
            return new Integer(i);
    }
    public final class Boolean {
        
    public static Boolean valueOf(boolean b) {
            
    return (b ? TRUE : FALSE);
        }
  • 相关阅读:
    git-format-patch如何指定补丁生成的Subject格式
    openwrt生成的交叉编译器在哪里
    git如何在自动生成补丁时指定补丁名的起始编号
    hyper-v安装虚拟机ubuntu 18.04 64bit后无法使能增强模式怎么办
    Best regards缩写是什么
    git如何自动打补丁
    ubuntu 18.04 64bit build tensorflow report error:C++ compilation of rule '//tensorflow/core/kernels:broadcast_to_op' failed (Exit 4)
    linux安装yaml时出现Could not find a version that satisfies the requirement yaml (from versions: ) No matching distribution found for yaml
    String.format保留小数位数
    BigDecimal的保留位数和四舍五入的方法
  • 原文地址:https://www.cnblogs.com/csxf/p/3407470.html
Copyright © 2011-2022 走看看