装箱(Autoboxing)和拆箱(Unboxing)

装箱(Autoboxing)和拆箱(Unboxing)

wiki

学习目标

  • Why:为什么需要装箱和拆箱;
    • 代码更加简洁
    • 基本数据类型可以使用泛型
  • What:什么是装箱和拆箱;
  • How:装箱和拆箱是怎么实现的?
    • 装箱:包装类的valueOf()方法
    • 拆箱:包装类的***Value()方法
  • When:什么时候使用装箱和拆箱
  • Where:在什么地方会使用到装箱和拆箱
  • Other:装箱和拆箱的优点和缺点
    • 优点:代码简洁,可以使用泛型
    • 缺点:装箱需要创建新的对象,时间会比较长

装箱和拆箱的定义

  • 装箱(Autoboxing):基本数据类型(int,double等)转换为对应的包装类叫做装箱;
    • 基本数据类型作为参数传递给一个用对应包装类作为参数的方法时使用装箱;
    • 将基本数据类型赋值给对应的包装类;
  • 拆箱(Unboxing):包装类转换为对应的基本数据类型叫做拆箱;
    • 将包装类作为参数传递给一个用对应基本数据类型作为参数的方法时使用装箱;
    • 将包装类赋值给对应的基本数据类型;
Interger i = 10; //装箱
int num = i; //拆箱

基本数据类型和对应的包装类

Primitive Type Wrapper Class
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double

为什么需要拆箱和装箱

  • 在泛型中,编译器会进行类型擦除,将泛型中的类型编译为Object;对应基础数据类型,无法使用泛型,比如创建列表,即不能出现List<int>, List<long>;通过装箱和拆箱,可以将int类型加入List<Integer>中,其他类型类似

    public static void main(String[] args)
    {
        int i = 1;
        List<Integer> list = new ArrayList<Integer>();
        list.add(i);
    }
    

    编译后代码

    public static void main(String[] args) {
        int i = 1;
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(Integer.valueOf((int)i));
    }
    

装箱和拆箱是怎么实现的?

public static void main(String[] args)
{
    Integer i = 10;

    int n = i;
}
  • 使用命令java -jar .\cfr_0_132.jar .\BoxingTest.class --sugarboxing false > BoxingTest.txt反编译上述代码

    public static void main(String[] args) {
            Integer i = Integer.valueOf((int)10);
            int n = i.intValue();
        }
    
  • 装箱是利用包装类的valueOf()方法;

  • 拆箱是利用包装类的***Value()方法;

包装类的valueOf()方法

public static void main(String[] args) {

    Integer i1 = 100;
    Integer i2 = 100;
    Integer i3 = 200;
    Integer i4 = 200;

    System.out.println(i1==i2); // true
    System.out.println(i3==i4); // false
}
  • 查看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方法可以看出,如果数字在[-127,128]之间,不会新建Integer类,会直接返回缓存中已经创建的对象的引用;
public static void main(String[] args)
{
    Double d1 = 10.0;
    Double d2 = 10.0;

    System.out.println(d1 == d2); // false
}
  • 查看Double.valueOf()方法

    public Double(double value) {
        this.value = value;
    }
    
    • 由于double类型的值不是离散的,无法使用缓存保存;
  • Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的

    类型 包装类缓存范围
    Integer,Byte,Short,Long [-128,127]
    Character [0,127]
    Double,Float
  • Double、Float的valueOf方法的实现是类似的

when and where: 使用装箱和拆箱

public static void main(String[] args)
{
    Integer i1 = 1;
    Integer i2 = 2;
    Integer i3 = 3;
    System.out.println(i3 == (i1 + i2)); // true

    Long l1 = 3l;
    Long l2 = 2l;
    System.out.println(l1 == (i1 + i2)); // true
    System.out.println(l1.equals(i1 + i2)); // false
    System.out.println(l1.equals(i1 + l2)); // true
}

cfr编译后的代码

public static void main(String[] args) {
    Integer i1 = Integer.valueOf((int)1);
    Integer i2 = Integer.valueOf((int)2);
    Integer i3 = Integer.valueOf((int)3);
    System.out.println((boolean)(i3.intValue() == i1.intValue() + i2.intValue()));
    
    Long l1 = Long.valueOf((long)3L);
    Long l2 = Long.valueOf((long)2L);
    System.out.println((boolean)(l1.longValue() == (long)(i1.intValue() + i2.intValue())));
    System.out.println((boolean)l1.equals((Object)Integer.valueOf((int)(i1.intValue() + i2.intValue()))));
    System.out.println((boolean)l1.equals((Object)Long.valueOf((long)((long)i1.intValue() + l2.longValue()))));
}
  • 当 "=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象;而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程);
  • 包装器类型中equals()方法中并不会进行类型转换。

三目运算符中的装箱和拆箱

public static void main(String[] args)
{
    Map<String,Boolean> map =  new HashMap<String, Boolean>();
    Boolean b = (map!=null ? map.get("test") : false);
}
  • jdk1.7编译结果

    public static void main(String[] args) {
        HashMap map = new HashMap();
        Boolean b = Boolean.valueOf((boolean)(map != null ?((Boolean)map.get((Object)"test")).booleanValue() : false));
    }
    
    • 由于jdk1.7中,第二和第三操作数为基本类型和对象时,会将对象拆箱,所有上述代码执行时会报空指针的错误;
  • jdk1.8编译结果

    public static void main(String[] args) {
        HashMap map = new HashMap();
        Boolean b = map != null ? (Boolean)map.get((Object)"test") : Boolean.valueOf((boolean)false);
    }
    
    • 理解要点

      • 三目运算符第二个和第三个操作数的类型不同时返回的类型到底是什么?
      • 第二个和第三个操作数会进行什么操作?
    • 在java1.8中,操作数共有18中,基本数据类型和对应的包装类共16个,加上null和Object共18个,所有表达式返回的数据类型组合共有324种情况。

    • lub()的理解

      least upper bound - basically the closest superclass that they have in common; since null (type "the special null type") can be implicitly converted (widened) to any type, you can consider the special null type to be a "superclass" of any type (class) for the purposes of lub().

      public static void main(String[] args)
      {
          Map<String,Boolean> map =  new HashMap<String, Boolean>();
          Boolean flag = true;
          Integer i = 1;
          Comparable res = map!=null ? flag : i;
          System.out.println(res);
      }
      

      如果第二个操作数是Boolean,第三个操作数是Integer,则三目运算符的返回类型是lub(Boolean,Integer)

      // 编译后的结果
      public static void main(String[] args) {
          HashMap map = new HashMap();
          Boolean flag = Boolean.valueOf((boolean)true);
          Integer i = Integer.valueOf((int)1);
          Comparable res = (Comparable)(map != null ? (Comparable<Boolean>)flag : (Comparable<Boolean>)i);
          System.out.println((Object)res);
      }
      

      lub的作用是需找两个对象的最小共同父类或者父接口,通过查看BooleanInteger代码可以得到lub是Comparable接口

      public final class Boolean implements java.io.Serializable,
                                            Comparable<Boolean>{}
      
      public final class Integer extends Number implements Comparable<Integer> {}
      
    • bnp(..): bnp(..) means to apply binary numeric promotion.

      When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:

      1. If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
      2. Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
        • If either operand is of type double, the other is converted to double.
        • Otherwise, if either operand is of type float, the other is converted to float.
        • Otherwise, if either operand is of type long, the other is converted to long.
        • Otherwise, both operands are converted to type int.

      Binary numeric promotion is performed on the operands of certain operators:

    • T | bnp(..):The form "T | bnp(..)" is used where one operand is a constant expression of type intand may be representable in type T, where binary numeric promotion is used if the operand is not representable in type T.

      如果一个操作数是int的常量(即1,2,...,不是int变量),并且int常量的范围在类型T的范围内,则返回类型T,否则返回bnp(int,T)

      以第二个操作数为short,第三个操作数为int为例,返回类型为short| bnp(short,int)

      public static void main(String[] args)
      {
          // 以操作数二为short类型,操作三为int类型为例
          Map<String,Boolean> map =  new HashMap<String, Boolean>();
          short b1 = 1;
          int i1 = 2;
          short test = (map!=null ? b1 : 32767); // 编译不报错, short类型的范围是[-32768,32767],所有32767可以转化为short类型
          short test1 = (map!=null ? b1 : 32768); //编译报错,32768不可以转化为short类型
          short test2 = (map!=null ? b1 : i1); //编译报错,i1是符号引用,在编译器无法确定具体的值,无法确定是否可以转化为short类型。
      }
      
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容

  • 引入 == 基本数据类型(也称原始数据类型) :byte,short,char,int,long,float,do...
    凯诺婷阅读 1,181评论 1 7
  • 自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象。自动装箱与拆箱的机制可以让我...
    codersm阅读 412评论 0 0
  • 1、什么是装箱?什么是拆箱?Java为每种基本数据类型都提供了对应的包装器类型,在Java 1.5之前如果要生成一...
    高岳_bdf5阅读 345评论 0 0
  • 今天去面试,负责人是朋友的朋友,一见面居然是一个课程的小组长。而且比我小3岁。我有点蒙,这个世界是怎么了,我不过是...
    幸福女人成长阅读 99评论 0 0
  • 这是我第一次使用简书,我想记录我的生活❤今天我参加了我的寝室聚会,我的寝室6个人,人人都可以玩的来,所以我们关系很...
    经历晨曦阅读 205评论 0 0