• 关于java的自动拆装箱若干细节问题


    一、首先需要了解的几个前提

    1、自动装箱过程是通过调用valueOf方法实现(如Integer.valueOf(10)),而拆箱过程是通过调用包装器的 xxxValue方法实现(如Integer.intValue(a))。

    例如代码:

            Integer a = 1;
            a++;

    其自动装箱和拆箱的过程如下:

    复制代码
     0: iconst_1
     1: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
     4: astore_1
     5: aload_1
     6: invokevirtual #22                 // Method java/lang/Integer.intValue:()I
     9: iconst_1
    10: iadd
    11: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    复制代码

    2、缓存

    Integer、Short、Byte、Character、Long包装类型有缓存机制(cache数组)。

    Boolean类型有TRUE 和 FALSE两个静态成员。

    复制代码
        public static final Boolean TRUE = new Boolean(true);
    
        /** 
         * The <code>Boolean</code> object corresponding to the primitive 
         * value <code>false</code>. 
         */
        public static final Boolean FALSE = new Boolean(false);
    复制代码

    Double和Float没有缓存机制。

    二、关键的总结写在前面

    1、 我们知道,"=="运算符既可以用来比较基本类型变量和引用类型变量。当两个操作数都是包装器类型的变量时,判定标准为他们是否指向同一个对象;而如果其中有一个操作数是表达式(即包含算术运算)则会先进行自动拆箱,再进行对应基本类型变量比较。

    2、基本包装类型重写了equals方法,基本包装类型的equals不会进行类型的转换,类型不同的包装类型的变量直接被判定为false,尽管他们的数值有可能是相等的。

    三、从一段代码开始

    复制代码
    package com.demo;
    public class Interview {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int i = 3;
            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 250;
            Integer f = 250;
            Long g = 3L;
            Long h = 2L;
            
            System.out.println(i==(a+b));
            System.out.println(c==d);
            System.out.println(e==f);
            System.out.println(c.equals(d));
            System.out.println(e.equals(f));
            System.out.println(c==(a+b));
            System.out.println(c.equals(a+b));
            System.out.println(g==(a+b));
            System.out.println(g.equals(c));
            System.out.println(g.equals(a+b));
            System.out.println(g.equals(a+h)); 
        }
    }
    复制代码

    四、运行结果

    true
    true
    false
    true
    true
    true
    true
    true
    false
    false
    true
    View Code

    五、后面的解析有点长

    将如上代码编译后的class文件进行javap反汇编,基本能够说明大部分的问题。

    第一处:

    System.out.println(i==(a+b));

    执行过程如下:

     61: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     64: aload_3
     65: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     68: iadd
     69: if_icmpne     76

    说明分别对a和b进行了自动拆箱操作,然后再add运算,之后再与i进行比较,属于数值比较。

    第二处:

    System.out.println(c==d);

    这个大家都知道,在对3和3进行自动装箱成c和d时,直接从Integer的缓存数组cache中取得相同的对象引用,因此==判定返回true。

    Integer的valueOf(int i)源码如下:

    复制代码
    public static Integer valueOf(int i) {
        final int offset = 128;
        if (i >= -128 && i <= 127) { // must cache 
            return IntegerCache.cache[i + offset];
        }
            return new Integer(i);
     }
    复制代码

    第三处:

    System.out.println(e==f);

    与第二处不同的是,将250自动装箱时,不在-128和127之间,实际上是new了两个不同的对象。

    调用了两次如下的Integer的构造器,尽管传入的参数value都是250,但是确实是不同的对象。因此==判定返回false。

        public Integer(int value) {
          this.value = value;
        }

    第四处和第五处:

            System.out.println(c.equals(d));
            System.out.println(e.equals(f));

    这两处和第二和第三处形成对比,主要的区别是Integer重写了从Object继承下来的equals方法。

    Object的equals源码如下,简单干净,纯粹等同于对象的==比较:

        public boolean equals(Object obj) {
        return (this == obj);
        }

    Integer的equals源码如下:

        public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
        }

    可以发现,如果obj如果符合instanceof判定,那么会将obj自动拆箱,实际比较的是两个Integer对象的数值。

    这就解释了虽然e和f是不同的对象(==判定为false),但是equals判定为true,因为其数值都为250。

    第六处:

    System.out.println(c==(a+b));

    执行过程如下:

    复制代码
     145: aload         4
     147: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     150: aload_2
     151: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     154: aload_3
     155: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     158: iadd
     159: if_icmpne     166
    复制代码

    可以看到对a、b和c都调用Integer.intValue()方法进行自动拆箱,最终比较的仍然是数值,因此返回true。

    第七处:

    System.out.println(c.equals(a+b));

    执行过程如下:

    复制代码
     173: aload         4
     175: aload_2
     176: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     179: aload_3
     180: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     183: iadd
     184: invokestatic  #23                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
     187: invokevirtual #52                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
    复制代码

    可以看到其过程为:先对a和b进行自动拆箱,再进行add运算后,再对运算结果进行自动装箱,最后调用Integer.equals()方法进行判定,而equals判定最终比较的还是数组,因此返回true。

    第八处:

    System.out.println(g==(a+b));

    其执行结果如下:

    复制代码
    196: aload         8
    198: invokevirtual #55                 // Method java/lang/Long.longValue:()J
    201: aload_2
    202: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
    205: aload_3
    206: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
    209: iadd
    210: i2l
    211: lcmp
    复制代码

    与第六处的原理基本类似,都是进行自动拆箱,只不过这里g是调用Long.longValue()方法拆箱。

    第九处:

    System.out.println(g.equals(c));

    这个地方应当特别注意,观察Integer的equals源码不难发现,equals判定的第一步是进行instanceof判定,显然c不是Long的实例,判定失败,直接返回false。

    也就是说这里根本没有进行数值比较的机会。

    因此,基本包装类型的equals不会进行类型的转换,类型不同的包装类型对象直接被判定为false,尽管他们的数值有可能是相等的。

    第十处:

    System.out.println(g.equals(a+b));

    执行过程如下:

    复制代码
     239: aload         8
     241: aload_2
     242: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     245: aload_3
     246: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     249: iadd
     250: invokestatic  #23                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
     253: invokevirtual #59                 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
    复制代码

    这其实是第九处的扩展。

    不难看出,其执行过程为:先对a和b进行自动拆箱,进行add运算,在对结果进行自动装箱,再进行equals判定。

    但是add运算的结果自动装箱后依然是Integer类型,由第九处可知,自然会被判定为false。

    第十一处:

    System.out.println(g.equals(a+h)); 

    执行过程如下:

    复制代码
     262: aload         8
     264: aload_2
     265: invokevirtual #42                 // Method java/lang/Integer.intValue:()I
     268: i2l
     269: aload         9
     271: invokevirtual #55                 // Method java/lang/Long.longValue:()J
     274: ladd
     275: invokestatic  #31                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
     278: invokevirtual #59                 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
    复制代码

    与第十处不同之处在于,a和h自动拆箱后进行add运算的会向上转型为long类型,在对其自动装箱后自然会被包装成Long类型。

    同时,两个比较对象的数值相等,自然会被判定为true。

    六、最后附上完整的反编译代码

    D:javaqcworkspace	estincomdemo>javap -c Interview
    警告: 二进制文件Interview包含com.demo.Interview
    Compiled from "Interview.java"
    public class com.demo.Interview {
      public com.demo.Interview();
        Code:
           0: aload_0
           1: invokespecial #8                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: iconst_3
           1: istore_1
           2: iconst_1
           3: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
           6: astore_2
           7: iconst_2
           8: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
          11: astore_3
          12: iconst_3
          13: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
          16: astore        4
          18: iconst_3
          19: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
          22: astore        5
          24: sipush        250
          27: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
          30: astore        6
          32: sipush        250
          35: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
          38: astore        7
          40: ldc2_w        #22                 // long 3l
          43: invokestatic  #24                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
          46: astore        8
          48: ldc2_w        #29                 // long 2l
          51: invokestatic  #24                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
          54: astore        9
          56: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
          59: iload_1
          60: aload_2
          61: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
          64: aload_3
          65: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
          68: iadd
          69: if_icmpne     76
          72: iconst_1
          73: goto          77
          76: iconst_0
          77: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
          80: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
          83: aload         4
          85: aload         5
          87: if_acmpne     94
          90: iconst_1
          91: goto          95
          94: iconst_0
          95: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
          98: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         101: aload         6
         103: aload         7
         105: if_acmpne     112
         108: iconst_1
         109: goto          113
         112: iconst_0
         113: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         116: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         119: aload         4
         121: aload         5
         123: invokevirtual #47                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
         126: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         129: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         132: aload         6
         134: aload         7
         136: invokevirtual #47                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
         139: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         142: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         145: aload         4
         147: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         150: aload_2
         151: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         154: aload_3
         155: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         158: iadd
         159: if_icmpne     166
         162: iconst_1
         163: goto          167
         166: iconst_0
         167: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         170: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         173: aload         4
         175: aload_2
         176: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         179: aload_3
         180: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         183: iadd
         184: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
         187: invokevirtual #47                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
         190: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         193: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         196: aload         8
         198: invokevirtual #51                 // Method java/lang/Long.longValue:()J
         201: aload_2
         202: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         205: aload_3
         206: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         209: iadd
         210: i2l
         211: lcmp
         212: ifne          219
         215: iconst_1
         216: goto          220
         219: iconst_0
         220: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         223: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         226: aload         8
         228: aload         4
         230: invokevirtual #55                 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
         233: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         236: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         239: aload         8
         241: aload_2
         242: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         245: aload_3
         246: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         249: iadd
         250: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    
         253: invokevirtual #55                 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
         256: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         259: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         262: aload         8
         264: aload_2
         265: invokevirtual #37                 // Method java/lang/Integer.intValue:()I
         268: i2l
         269: aload         9
         271: invokevirtual #51                 // Method java/lang/Long.longValue:()J
         274: ladd
         275: invokestatic  #24                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
         278: invokevirtual #55                 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
         281: invokevirtual #41                 // Method java/io/PrintStream.println:(Z)V
         284: return
    }
    View Code

    完!

    转载请注明原文出处:

    http://www.cnblogs.com/qcblog/p/7670159.html 

  • 相关阅读:
    请问发表的文章如何备份?
    .NET中利用XML来自动生成代码策略
    XPO使用随笔
    为方便做封包分析,做了个十六进制和字符互转的网页小工具
    前端人员如何模拟慢网速环境
    补充《动态加载外部.js文件时候,javascript的执行顺序问题》
    凡客诚品官方网站的前端改进建议
    "Cachecontrol”常见的取值private、nocache、maxage、mustrevalidate及其用意
    javascript跨域操作cookie的点滴记录
    HTTPS的七个误解(译文转载)
  • 原文地址:https://www.cnblogs.com/qcblog/p/7670159.html
Copyright © 2020-2023  润新知