java的封箱和拆箱

Posted 白话灵煕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java的封箱和拆箱相关的知识,希望对你有一定的参考价值。

1、基本概念

字节的单位:byte。位的单位:bit,1byte=8bit 

2、8种基本数据类型

4种整型,2种浮点类型,1种用于表示Unicode编码的字符单元的字符类型和1种用于表示真值的boolean类型。

数据类型 浮点型大小(占字节数,2的几次方)  范围  默认值  包装器类型
byte(字节) 8  -128 - 127 0 Byte
shot(短整型) 16 -32768 - 32768 0 Short
int(整型) 32  -2147483648-2147483648 0 Integer
long(长整型) 64 -9233372036854477808-9233372036854477808 0 Long
float(浮点型)  32 -3.40292347E+38-3.40292347E+38 0.0f Float
double(双精度) 64 -1.79769313486231570E+308-1.79769313486231570E+308 0.0d Double
char(字符型) 16 ‘ \\u0000 - u\\ffff ’  ‘\\u0000 ’ Character
boolean(布尔型) 1 true/false false Boolean

 

 

3、包装类

  包装类即使把基本类型变成对象类型,包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法。

4、包装类转换关系
  基本类型  -->  包装器类
    Integer obj=new Integer(10);
  包装器类 -->  基本类型
    int num=obj.intValue();
  字符串 --> 包装器类
    Integer obj=new Integer("100");
  字符串   -->   基本类型
    int num=Integer.parseInt("-45.36");

5、自动装包/拆包(Autoboxing/unboxing)

     基本类与包装类的转换,则是装包与拆包
  自动装包/拆包大大方便了基本类型数据和它们包装类地使用。
  自动装包:基本类型自动转为包装类.(int >> Integer)
  自动拆包:包装类自动转为基本类型.(Integer >> int)

    int 是基本类型,直接存储数值 
  Integer是对象类,new一个对象时用一个引用指向这个对象
    Java把内存划分成两种:一种是栈内存,另一种是堆内存 
    在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,而实际的对象是在存储堆内存中 
    比如
    int i = 10;       ----直接在栈中分配空间 
    Integer j = new Integr(5);     ----对象是在堆内存中,而j(引用变量)是在栈内存中 
 在堆中分配的内存,由java虚拟机的自动垃圾回收器来管理。 因为在堆中分配空间所需的时间远大于从栈中分配存储空间

 

一.什么是装箱?什么是拆箱?

二.装箱和拆箱是如何实现的

三.面试中相关的问题

一.什么是装箱?什么是拆箱?

  在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料。在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:

Integer i = new Integer(10);

而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了:

Integer i = 10;

这个过程中会自动根据数值创建对应的 Integer对象,这就是装箱。

  那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型:

Integer i = 10;  //装箱

int n = i;   //拆箱
简单一点说,装箱就是  自动将基本数据类型转换为包装器类型;拆箱就是  自动将包装器类型转换为基本数据类型。
 
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 Main {
    public static void main(String[] args) {
         
        Integer i = 10;
        int n = i;
    }
}
 
反编译class文件之后得到如下内容:
 

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

  其他的也类似,比如Double、Character,不相信的朋友可以自己手动尝试一下。

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

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

三.面试中相关的问题

  虽然大多数人对装箱和拆箱的概念都清楚,但是在面试和笔试中遇到了与装箱和拆箱的问题却不一定会答得上来。下面列举一些常见的与装箱/拆箱有关的面试题。

1.下面这段代码的输出结果是什么?

答案是i1=i2  true;

i3=i4 false;

为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象.

只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现.

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

上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。

2.下面这段代码的输出结果是什么?

false
false

 

至于具体为什么,读者可以去查看Double类的valueOf的实现。

  在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

  注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。

     Double、Float的valueOf方法的实现是类似的。

 

Boolean i11 = false;
Boolean i21 = false;
Boolean i31 = true;
Boolean i41 = true;

System.out.println(i11==i21);
System.out.println(i31==i41);

 

true
true

 

4.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。

  当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:

  1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

  2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。

 

 
 
   Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
         
        System.out.println(c==d);
        System.out.println(e==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(a+b));
        System.out.println(g.equals(a+h));
 

true
false
true
true
true
false
true

这里面需要注意的是:当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。明白了这2点之后,上面的输出结果便一目了然。

第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

 
 
 
 
 
 
 
 
 
 

以上是关于java的封箱和拆箱的主要内容,如果未能解决你的问题,请参考以下文章

Java中装箱和拆箱的代码

细说JavaJava封箱拆箱的一些问题

java 自动装箱和拆箱

java 啥是拆箱和装箱,拆箱和装箱 嘛用啊???

深入剖析Java中的装箱和拆箱

深入剖析Java中的装箱和拆箱