Skip to content

java中的包装类型、equals及自动装箱拆箱问题

为什么要设置包装类?

java是面向对象的语言,自从1996年Java发布以来,基本数据类型就是Java语言的一部分。但是八大基本数据类型却不是面向对象的,不过java提供了这些基本数据类型的包装类:

 基本类型     包装器类型  
boolean Boolean
char Character
int Integer
byte Byte
short Short
long Long
float Float
double Double

在java中,基本数据类型是存放在栈中的,而new出来的对象是存放在堆中的,相比堆,栈的存取速度更快,效率更高。那既然如此为什么还要设置包装类?

首先,基本类型并不具有对象的性质,为了让它具有对象的性质,就出现了包装类,包装类中封装了属性和方法,丰富了基本数据类型的的操作。

例如当需要向ArrayList,HashMap中存放东西时,像int,double这种基本类型是无法放进去的,因为它们都是存放对象的,这时就需要这些包装类了。

另外定义泛型时也不支持基本类型, 如:

List<int> list = new ArrayList<int>(); //不可以

List<Integer> list = new ArrayList<Integer>(); //可以

基本类型和包装类的区别

1. 声明方式不同:

基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;

2. 存储方式及位置不同:

基本类型直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;

3. 初始值不同:

基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;

4. 占用内存空间不同:

基本数据类型例如double总是占据内存的64个位,而引用类型如Double取决于JVM,例如我的操作系统是64位的,jvm也是64位的,但Double就要占据24个字节,对象的引用占8个字节,对象中double的值占8个字节,对Double对象的引用占8个字节,就整整比double多占了16个字节!

5. 使用场景不同:

基本类型多用在需要大量计算的场景,其性能要比包装类高很多。

==和equals

先给一个题:

在jdk1.5的环境下,有如下几条语句:

Integer a1 = 59;

int a2 = 59;

Integer a3 =Integer.valueOf(59);

Integer a4 = new Integer(59);

Long b = 59l;

Integer a5 = 256;

Integer a6 = 256;

以下输出结果为false的是:

System.out.println(a1== a2);

System.out.println(a1== a3);

System.out.println(a3== a4);

System.out.println(a2== a4);

System.out.println(b.equals(a2));

System.out.println(a5 == a6);

1.  ==

  1. 对于基本类型,比较的是基本类型的值;
  2. 对于对象,比较的是两个对象在内存中的地址。

2.  equals

equals是Object对象中的方法,在Object类中:

可以看出比较的是引用是否相等,即比较的是地址。

java中所有的类都继承自Object,所以一般会重写equals方法,实现不同,比较的方式就不同,下面看看Integer类的实现:

首先判断比较的对象是否是Integer的实例,若是,将其强制转换为Integer类型,然后再比较它们的值是否相等,若不是Integer的实例,直接返回false。

Integer的缓存机制

在jdk1.5之后,所有整数类型的包装类都有缓存机制,其缓存类是包装类的一个静态内部类,范围如下:

包装类 缓存类 范围
Character CharacterCache 0~127
Byte ByteCache -128~127
Short ShortCache -128~127
LongCache LongCache -128~127
Integer IntegerCache -128~127

 

看看IntegerCache的定义:

自动装箱和拆箱

什么时自动装箱和拆箱?

自动装箱和拆箱从Java 1.5开始引入,目的是将基本类型值自动地转换成对应的对象。比如将int类型的变量转换为Integer对象,叫做装箱,将Integer对象转换为int类型的值,叫做拆箱。

自动装箱时编译器将调用valueOf()方法将原始类型值转换为对象,如Integer类:

首先判断int值是否在缓存中,若是,直接返回缓存对象,若否,则new一个新的Integer对象。

自动拆箱时将调用诸如intValue()之类的方法,将对象转换为原始类型的值,如Integer类:

何时发生自动装箱与自动拆箱?

1.赋值时

在jdk1.5之前,若要创建一个Integer对象并赋值,需要这样:

Integer x = new Integer(5); 

//或:

Integer y = Integer.valueOf(5);

若要用Integer对象得到int值,需要这样:

Integer a = Integer.valueOf(3);

int b = a.intValue();

而在jdk1.5之后,编译器可以自动帮我们进行转换,可以直接:

Integer x = 5;  //autoboxing

int y = x;   //unboxing

2.方法调用时

在jdk1.5之前,若要在ArrayList、HashMap中添加原始数据类型的元素,需要手动将其转换为对应的包装类,而在jdk1.5之后,编译器可以自动帮我们进行转换,如:

List<Integer> list = new ArrayList<Integer>();

int a = 5;

list.add(a);   //autoboxing

int b = list.get(0);  //unboxing

又如:

public static Integer show(Integer arg){
    return arg;
}
 
show(3); //自动装箱
int result = show(3); //由于返回值是Integer类型的,所以可以自动拆箱

题解

现在再看看开头那个题:

a1== a2 

a1是 Integer 对象, a2 是 int ,这里比较的不是地址,而是值。 Integer 会自动拆箱成 int ,然后进行值的比较。结果为true。

a1== a3 

由于59在IntegerCache中,所以直接返回引用,a1和a3是同一个对象,结果为true。

a3== a4

a3是IntegerCache中的对象,而a4是new的对象,两者不是同一个对象,结果为false。

a2== a4 

a4是 Integer 对象, a2 是 int ,这里比较的不是地址,而是值。 Integer 会自动拆箱成 int ,然后进行值的比较。结果为true。

b.equals(a2)

由于b是Long对象,这里的equals调用的是Long的equals方法,先对a2进行自动装箱,转换为Integer对象,但是由于Integer对象不是Long类的实例,所以直接返回false。

a5 == a6

由于256超出了缓存的范围,所以a5和a6都是new出来的对象,两者不是同一个对象,结果为false。

 

另外有一个小技巧:

JVM中一个字节以下的整型数据会在JVM启动的时候加载进内存,除非用new Integer()显式地创建对象,否则都是同一个对象。

 

本文参考自:

http://www.importnew.com/11915.html

http://www.importnew.com/15712.html

 

 

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注