zoukankan      html  css  js  c++  java
  • 【细说Java】Java封箱拆箱的一些问题

    1.概念

    首先简单介绍一下概念性的东西:

    所谓封箱:就是把基本类型封装成其所对应的包装类型;

    而拆箱则恰好相反,是把包装类型转换成其所对应的基本数据类型。

    如基本类型int,封箱后的包装类是Integer。

    2.包装类的缓存值,equals与==

      相信,大家对equals与==一定很熟悉了吧,同样,包装类也重写了equals方法,只要两个包装类对象所包装的基本类型数据是相等的,那么,则认为两个包装类对象是相等的;

      然后,看一段简单的程序,并预测以下它的打印结果:

    public class Test {
        public static void main(String[] args) {
            System.out.println("---------------------");
            testBooleanBoxing();
            System.out.println("---------------------");
            testByteBoxing();
            System.out.println("---------------------");
            testShortBoxing();
            System.out.println("---------------------");
            testIntegerBoxing();
            System.out.println("---------------------");
            testFloatBoxing();
        }
    
        public static void testFloatBoxing() {
            Float float1 = 10F;
            Float float2 = 10F;
            Float float3 = 300F;
            Float float4 = 300F;
            System.out.println("float1.equals(float2) : " + float1.equals(float2));
            System.out.println("float1 == float2 : " + (float1 == float2));
            System.out.println("float3.equals(float4) : " + float3.equals(float4));
            System.out.println("float3 == float4 : " + (float3 == float4));
        }
    
        public static void testIntegerBoxing() {
            Integer int1 = 10;
            Integer int2 = 10;
            Integer int3 = 300;
            Integer int4 = 300;
            System.out.println("int1.equals(int2) : " + int1.equals(int2));
            System.out.println("int1 == int2 : " + (int1 == int2));
            System.out.println("int3.equals(int4) : " + int3.equals(int4));
            System.out.println("int3 == int4 : " + (int3 == int4));
        }
    
        public static void testByteBoxing() {
            Byte byte1 = -128;
            Byte byte2 = -128;
            Byte byte3 = 127;
            Byte byte4 = 127;
            System.out.println("byte1.equals(byte2) : " + byte1.equals(byte2));
            System.out.println("byte1 == byte2 : " + (byte1 == byte2));
            System.out.println("byte3.equals(byte4) : " + byte3.equals(byte4));
            System.out.println("byte3 == byte4 : " + (byte3 == byte4));
        }
    
        public static void testShortBoxing() {
            Short short1 = -128;
            Short short2 = -128;
            Short short3 = 128;
            Short short4 = 128;
            System.out.println("short1.equals(short2) : " + short1.equals(short2));
            System.out.println("short1 == short2 : " + (short1 == short2));
            System.out.println("short3.equals(short4) : " + short3.equals(short4));
            System.out.println("short3 == short4 : " + (short3 == short4));
        }
    
        public static void testBooleanBoxing() {
            Boolean bool1 = true;
            Boolean bool2 = true;
            Boolean bool3 = true;
            Boolean bool4 = true;
            System.out.println("bool1.equals(bool2) : " + bool1.equals(bool2));
            System.out.println("bool1 == bool2 : " + (bool1 == bool2));
            System.out.println("bool3.equals(bool4) : " + bool3.equals(bool4));
            System.out.println("bool3 == bool4 : " + (bool3 == bool4));
        }
    }

    然后看以下结果:

    ---------------------
    bool1.equals(bool2) : true
    bool1 == bool2 : true
    bool3.equals(bool4) : true
    bool3 == bool4 : true
    ---------------------
    byte1.equals(byte2) : true
    byte1 == byte2 : true
    byte3.equals(byte4) : true
    byte3 == byte4 : true
    ---------------------
    short1.equals(short2) : true
    short1 == short2 : true
    short3.equals(short4) : true
    short3 == short4 : false
    ---------------------
    int1.equals(int2) : true
    int1 == int2 : true
    int3.equals(int4) : true
    int3 == int4 : false
    ---------------------
    float1.equals(float2) : true
    float1 == float2 : false
    float3.equals(float4) : true
    float3 == float4 : false

    不知道大家预测的结果和实际结果是否是相同的;

      或许有些人有疑问,不同的值对于同一包装类来说,使用 == 运算符得出的结果却是不同的,这是为什么呢?

      这就要从包装类的不可变性说起。先说以下String,因为String类的对象创建后不可改变,所以多个引用指向同一个String也不会有什么危险,因此,在编译阶段会将String常量放入字符串常量池中,当下次使用时就可以直接从字符串常量池中提取。

      而包装类和String类似,对象同样是不可变的,所以对于某些频繁使用的值,系统提供了包装类的缓存(即预先创建了经常使用的包装类对象,可以参考包装类的valueOf方法),当需要时直接从缓存中提取,而不是再创建一个新的包装类对象。这样当重复使用的时候就可以避免重复创建对象造成的资源浪费。而这些缓存的包装类的值如下:

    • boolean的所有值(true 和 false);
    • byte的所有制(-128 ~ 127);
    • char值的(0 ~ 127);
    • short值的(-128 ~ 127);
    • int值的(-128 ~ 127);
    • long值的(-128 ~ 127);

    其中,浮点数类型float和double,包装类没有缓存。

    如以上int类型,因为10在Integer的缓存范围内,所以

    Integer int1 = 10;
    Integer int2 = 10;

      两次对10的封箱使用的都是缓存中包装数值为10的Integer对象(这些对象被封装在IntegerCache类中),因此int1和int2指向的相同的对象,所以不论是 equals 还是 == ,运算的结果都是true;

      而数值300则不在缓存的范围内,因此并不是使用缓存中的对象,而是新建两个Integer对象,所以 int3 和 int4 使用 == 运算符的结果为false,其他的情况与Integer类似。

      如果要了解详细的缓存值,可查看源码中相应的Cache即可,如char值的可查看CharacterCache类,long值的可查看LongCache类;

    3. 拆箱还是封箱

      在Java中 == 和 != 都可以应用于任何类型,但如果这两个二元操作符的两个操作数一个是基本数据类型,而另一个是包装类,那这是会如何操作呢?

    int x = 5;
    Integer y = 5;
    boolean bool = (x == y);

      如果要计算 x== y,那么,是将x封箱成Integer类型与y进行比较,还是将y拆箱成int类型与x进行比较呢?

      我们看下面代码:

       public static void main(String[] args) {
            int int1 = 200;
            Integer int2 = 200;
            if (int1 == int2) {
                System.out.println("拆箱操作");
            } else {
                System.out.println("封箱操作");
            }
    
            int int3 = 10;
            Integer int4 = new Integer(10);
            if (int3 == int4) {
                System.out.println("拆箱操作");
            } else {
                System.out.println("封箱操作");
            }
            
            //混合类型比较
            short short1 = 200;
            Integer int5 = 200;
            if (short1 == int5) {
                System.out.println("拆箱操作");
            } else {
                System.out.println("封箱操作");
            }
        }    

    返回结果如下:

    拆箱操作
    拆箱操作
    拆箱操作

      结果已经一目了然了。当基本数据类型与包装类型进行 == 比较时,因为比较的是内存地址,所以根据结果来看,是将包装类拆箱成基本数据类型,然后对两个基本数据类型进行比较。

    4. 方法中的封箱与拆箱操作

    看下面的代码:

    public class Test {
        public static void main(String[] args) {
            Test test = new Test();
            int x = 10;
            test.box1(x);
            test.box2(x);
            test.box3(x);
        }
    
        public void box1(int x) {
            System.out.println("调用int参数的box1方法");
        }
    
        public void box1(float x) {
            System.out.println("调用float参数的box1方法");
        }
    
        public void box1(Integer x) {
            System.out.println("调用Integer参数的box1方法");
        }
    
        public void box2(float x) {
            System.out.println("调用float参数的box2方法");
        }
    
        public void box2(Integer x) {
            System.out.println("调用Integer参数的box2方法");
        }
    
        public void box3(Integer x) {
            System.out.println("调用Integer参数的box3方法");
        }
    }

    运行后,看以下结果:

    调用int参数的box1方法
    调用float参数的box2方法
    调用Integer参数的box3方法

      结果应该不会有什么问题,重载方面的问题我会单独拿出来再说。这里要说的是,把封箱操作置后考虑(即只有当选择不到合适参数的方法时,才考虑封箱操作),主要是为了兼容以前的版本。试想:

    public void method(float f) {}
    public void method(Integer i) {}

      如果要调用 method(7),那么在JDK5之前的版本,因为没有自动封箱,所以会调用float参数的method方法。这是大多数人都知道的,如果JDK5后规定优先考虑封箱操作的话,那么该程序就会调用Integer参数的method方法,这对大多数人来说,都是很尴尬的,以前的那些脑海里的规则都要改变了。所以,对于新增的自动封箱特性,只能置后考虑了。

    参考自:《细说Java》

  • 相关阅读:
    涨知识| 在国内,如何顺利使用谷歌(转)
    css3网站收集
    淘宝客 新内容
    淘宝客工作计划
    淘宝客笔记
    java并发编程
    代理模式之远程代理
    动态代理
    模板方法模式
    Spring整合web开发
  • 原文地址:https://www.cnblogs.com/xiaozhang2014/p/5347407.html
Copyright © 2011-2022 走看看