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

    装箱与拆箱

    什么是装箱与拆箱

    描述

    语言描述,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

    代码描述就是:

        Integer integer = 100;  //自动装箱
        int i = integer;  //自动拆箱
    

    基本技术类型对应的包装器类型表:

    数据类型包装器类型
    int(4字节) Integer
    byte(1字节) Byte
    short(2字节) Short
    long(8字节) Long
    float(4字节) Float
    double(8字节) Double
    char(2字节) Character
    boolean(未定) Boolean

    如何实现装箱与拆箱

    装箱与拆箱的代码

    
    public class IntegerAndInt {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Integer integer = 100; //自动装箱
            int i = integer; //自动拆箱
        }
    
    }
    

    反编译class文件


    从反编译得到的字节码内容可以看出,在装箱的时候自动调用了Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

    因此,装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的xxxValue方法实现的。

    valueOf方法

     public static Integer valueOf(int i) {
         return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
     }
    

    首先判断数值大小,如果数值大于等于128或者小于-128,则创建一个Integer对象返回,否则,则返回SMALL_VALUES的i+128的数值。

    接着,查看以下Integer的构造函数:

    private final int value;
    
    public Integer(int value) {
        this.value = value;
    }
    
    public Integer(String string) throws NumberFormatException {
        this(parseInt(string));
    }
    

    Integer定义了一个value变量,创建一个Integer对象时,就会给这个变量初始化。第二个传入的是一个String变量,它会先把它转换成一个int值,然后进行初始化。

    再看一下SMALL_VALUES[i+128]是什么:

    private static final Integer[] SMALL_VALUES = new Integer[256];
    

    SMALL_VALUES是一个静态的Integer数组对象,也就是说valueOf返回的都是一个Integer对象。
    通过分析可以看到装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能。

    initValue方法

     @Override
     public int intValue() {
         return value;
     }
    

    intValue方法直接返回了value值。

    装箱与拆箱需要注意的一些问题

    例子一

    public class Main{
        public static void main(String[] args) {
            Integer i1 = 66, i2 = 66, i3 = 166, i4 = 166;
            System.out.println(i1 == i2);//true
            System.out.println(i3 == i4);//false
           }
    }
    

    看上面的代码可以发现,两个比较的结果不相同,再结合上面的装箱原理,128~-127的装箱是直接返回的SMALL_VALUES数组中存储的值,所以i1和i2的装箱结果返回的是同一个变量,所以是相等的,而i3和i4是返回的新创建的变量,两个变量是不同的,所以不相等。

    例子二

    Integer a = new Integer(6);
    Integer b = 6; // 将6自动装箱成Integer类型
    int c = 6;
    System.out.println(a == b); // false 两个引用没有引用同一对象
    System.out.println(a == c); // true a自动拆箱成int类型再和c比较
    

    a是一个创建的Integer对象,而b是自动装箱,是SMALL_VALUES数组中存储的值,所以a和b是不同的对象,不相等,而c是int类型的值,a与c相比是会先拆箱为int类型的6,两个6数值相比,故相等。

    例子三

        Integer i1 = new Integer(6);
        Integer i2 = 6;
        System.out.println("i1.equals(i2):"+(i1.equals(i2))); //true
    

    会发现同样的对象,==与equals的结果是不同,先看一下equals源码:

     @Override
     public boolean equals(Object o) {
         return (o instanceof Integer) && (((Integer) o).value == value);
     }
    

    发现equals方法是比较value值相同的,比较的内容的本身。

    例子四

    Integer num1 = 100;
    int num2 = 100;
    Long num3 = 200L;
    System.out.println(num1+num2); //200
    System.out.println(num3 == (num1+num2)); //true
    System.out.println(num3.equals(num1+num2)); //false
    System.out.println(num3 == (num1+num2)); //true
    

    当一个基本类型数据域封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
    而num3.equals(num1+num2)为false的原因是,num1+num2的类型不是Long,所以为false。
    Long的equals方法:

     @Override
     public boolean equals(Object o) {
         return (o instanceof Long) && (((Long) o).value == value);
     }
    

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

    例子五

      Integer integer=null;
      int i=integer;
    

    上面的代码可以通过编译,但是在运行时,就会抛出空指针异常。integer是Integer类型的对爱选哪个,可以指向null,但是对integer进行拆箱的时候,就是对一个null对象调用valueOf方发,所以会抛出空指针异常。

    注意

    Integer、Short、Byte、Charater、Long这几个类的valueOf方法的实现是类似的,Double和Float的valueOf方法的实现是类似的,是没有想Integer一样的SMALL_VALUE数组的。
    Double的valueOf方法:

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

    Float的valueOf方法:

    public static Float valueOf(String s) throws NumberFormatException {
        return new Float(FloatingDecimal.getThreadLocalInstance().readJavaFormatString(s).floatValue());
    }
    

    还有,Boolean的valueOf方法:

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

    所以不管对象是不是同一个,只要对象的值时相同的,就是相等的。

    5.总结

    Java通过自动装箱和拆箱的机制,节省了部分内存开销和创建对象的开销(Integer的128~-127常用值节省开支),提高了效率同时简化了代码,不用每次需要程序员转换类型。在比较数值相等时,尽量使用equals()方法。

    参考文章

    https://www.cnblogs.com/wang-yaz/p/8516151.html 详解Java的自动装箱与拆箱(Autoboxing and unboxing)
    https://www.cnblogs.com/dolphin0520/p/3780005.html 深入剖析Java中的装箱和拆箱

  • 相关阅读:
    VMware下Linux配置网络
    前端知识之查漏补缺二
    前端网络基础查漏补缺篇
    简单实现Promise
    前端知识之查漏补缺-1
    git tag
    云服务器安装node环境 mysql nginx
    js动画
    vue原理之双向绑定虚
    js的封装、继承与多态
  • 原文地址:https://www.cnblogs.com/zhangmiao14/p/9746301.html
Copyright © 2011-2022 走看看