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

    以前都没有听说过,这次看海子的博客,真是长见识了!

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

    简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

    1         //自动装箱
    2         Integer num = 3;
    3         //自动拆箱
    4         int count = num;

    在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

    因此可以用一句话总结装箱和拆箱的实现过程:

    装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

    下面这段代码是Integer的valueOf方法的具体实现:

        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }

    在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

    IntegerCache.cache[]是一个静态的Integer数组对象,也就是说最终valueOf返回的都是一个Integer对象。

    上面我们看到在Integer的构造函数中,它分两种情况: 

    1、i >= 128 || i < -128 =====> new Integer(i) 
    2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]

    1 static final Integer cache[] = new Integer[256];

    cache本来已经被创建好,也就是说在i >= 128 || i < -128是会创建不同的对象,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象。

    下面是几个例子:

     1 package lesson1213;
     2 
     3 public class TestZhuangXiang {
     4 
     5     public static void main(String[] args) {
     6         //自动装箱
     7         Integer num1 = 3;        
     8         Integer num2 = 3;
     9         
    10         Integer num3 = 200;
    11         Integer num4 = 200;
    12         
    13         System.out.println(num1==num2);  //true
    14         System.out.println(num3==num4);  //false
    15         
    16     }
    17 
    18 }

    如果是Double类呢?

    1         Double d1 = 1.0;
    2         Double d2 = 1.0;
    3         Double d3 = 200.0;
    4         Double d4 = 200.0;
    5         
    6         System.out.println(d1==d2);  //false
    7         System.out.println(d3==d4);  //false

    看看上面的执行结果,跟Integer不一样,这样也不必奇怪,因为它们的valueOf实现不一样,结果肯定不一样,那为什么它们不统一一下呢? 
    这个很好理解,因为对于Integer,在(-128,128]之间只有固定的256个值,所以为了避免多次创建对象,我们事先就创建好一个大小为256的Integer数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。

    但是对于Double类型来说,我们就不能这样做,因为它在这个范围内个数是无限的。 
    总结一句就是:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

    所以在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。

    1     public static Double valueOf(double d) {
    2         return new Double(d);
    3     }

    下面我们进行一个归类: 
    Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。 
    Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。

    下面对Integer派别进行一个总结,如下图: 
    这里写图片描述

    下面我们来看看另外一种情况:Boolean类

    1         Boolean b1 = true;
    2         Boolean b2 = true;
    3         Boolean b3 = false;
    4         Boolean b4 = false;
    5         System.out.println(b1==b2); //true
    6         System.out.println(b3==b4); //true

    可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象。

    1     public static Boolean valueOf(boolean b) {
    2         return (b ? TRUE : FALSE);
    3     }

    可以看到并没有创建对象,因为已经在内部提前创建好了两个对象,因为只有两种情况,这样也是为了避免重复创建太多对象。

    1     public static final Boolean TRUE = new Boolean(true);
    2     public static final Boolean FALSE = new Boolean(false);

    下面讲述下equal  和 == 在这几个类中的应用

     1 package lesson1213;
     2 
     3 public class Test {
     4     
     5     /*1:== 比较两个值是否相等。如果作用于基本数据类型,比较其值是否相等。
     6          * 如果作用于引用类型变量,则比较的是所指向对象的地址。
     7       2:在object中equal,equal方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。
     8       但是有一些类例如String,Double, Date,Interger等,都对equal方法进行了重写用来比较应用的对象所存储的值是否相等。
     9       注意equal不能用于基本数据类型。
    10     */    
    11     
    12     public static void main(String[] args) {
    13         Integer num1 = 200;
    14         int num2 = 200;
    15         System.out.println(num1==num2);         //true
    16         System.out.println(num2==num1);            //true    
    17         System.out.println(num1.equals(num2));  //true
    18         
    19         Integer a = 1;
    20         Integer b = 2;
    21         Integer c = 3;
    22         Integer d = 3;
    23         Integer e = 300;
    24         Integer f = 300;
    25         Long g = 3L;
    26         Long h = 2L;
    27         
    28         Integer e1 = 100;
    29         Integer e2 = 200;
    30         int e3=100;
    31         int e4=200;
    32         
    33         System.out.println(c==d);              //true,  没有疑问
    34         System.out.println(e==f);              //false, 超过范围-128~127,每次都重新new
    35         System.out.println(e==f+0);            //true, 如果有操作运算符,就是比较的值
    36         System.out.println(c==(a+b));          //true
    37         System.out.println(c.equals((a+b)));   //true
    38         System.out.println(g==(a+b));          //true   //有操作运算符
    39         System.out.println(g.equals((a+b)));   //false  //类型不一样
    40         System.out.println(g.equals((a+h)));   //true   //自动提升类型
    41         System.out.println(g==(a+h));          //true    
    42         System.out.println(e==(e1+e2));        //true
    43         System.out.println(e.equals((e1+e2))); //true
    44         System.out.println(e==(e1+e4));        //true
    45         System.out.println(e.equals((e2+e3))); //true
    46     }
    47 }

    总结:

    1:当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动  拆箱的过程)。

    补充:"=="只有两个都是包装类型,并且没有操作运算,才是比较是否指向同一个对象。如果有一个是包装类型,另一个不是包装类型,则比较的就是值。

              当两种不同类型用==比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱

              当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。

    2:equal, 首先判断类型是否一样,如果一样,就看值是否相等。对于包装器类型,equals方法并不会进行类型转换。

    下面是Integer中对equal进行重载:

    1     public boolean equals(Object obj) {
    2         if (obj instanceof Integer) {
    3             return value == ((Integer)obj).intValue();
    4         }
    5         return false;
    6     }

    line36由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。

    line37对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。

    下面这个例子有点奇怪,自己也不是很了解,但是程序运行结果就是这样:会自动提升类型,所以相等

     1         int e5 = 3;
     2         long e6 = 3;        
     3         //Long e66 = 3;                        //编译失败Type mismatch: cannot convert from int to Long
     4         Long e666=3L; 
     5         Double e7 = 3.0;
     6         Integer e8 = 3;
     7         
     8         System.out.println(e5==e6);            //true
     9         System.out.println(e5==e666);          //true
    10         System.out.println(e5==e7);            //true
    11         System.out.println(e6==e7);            //true, 一边是基础类型,一般是包装类,比较值
    12         System.out.println(e6==e8);            //true
    13         //System.out.println(e7==e8);          //编译失败Incompatible operand types Double and Integer

    到此处,应该对包装类和装箱,拆箱比较了解了,在遇见这样的题目应该没有问题了。

  • 相关阅读:
    Celery详解
    JWT详解
    进程及进程池
    多线程详解
    python常用模块之os模块的用法
    python常用模块之paramiko与ssh
    reflect 反射
    http 静态文件
    模板渲染语言
    http web 开发
  • 原文地址:https://www.cnblogs.com/beilou310/p/10115364.html
Copyright © 2011-2022 走看看