最近刷题的时候总会碰到各种关于基本数据类型的装箱和拆箱的问题,今天决定做一个总结。
Java的包装类
Java是面向对象的一门语言,然而Java中的基本数据类型却不是面向对象的,这使得在实际的使用这种会存在很多不便,为了弥补这一不足,Java在设计类的时候为每个基本数据类型都设置了一个类,这八个为基本数据类型设计的类就算包装类。
基本数据类型 大小 包装类
boolean / Boolean
byte 8bit/1个字节 Byte
short 16bit/2个字节 Short
int 32bit/4个字节 Integer
long 64bit/8个字节 Long
float 32bit/4个字节 Float
double 64bit/8个字节 Double
char 16bit/2个字节 Character
void / Void
包装类的作用:
1.提供了一种机制,使得基本数据类型也是面向对象的,从而是基本数据类型能够包含在为对象而保留的操作中,如添加到Collections中,或者从带对象返回值的方法中返回。在java5中增加了自动拆箱和装箱的功能。
2.为基本数据类型提供了分类的功能,这些功能大多数与各种转换有关,如:在基本值和String对象间相互转换,在基本值和String对象之间按不同基数转换,如二进制、八进制和十六进制。
包装类的共同方法:
1.带有基本值参数并创建包装类对象的构造函数。如利用Integer包装类创建对象,Integer obj=new Integer(145);
2.带有字符串参数并创建包装类对象的构造函数.如:new Integer(“-45.36”);
3.可生成对象基本值的typeValue方法,如:obj.intValue();
4.将字符串转换为基本值的parseType方法,如:Integer.parseInt(args[0]);
5.生成哈稀表代码的hashCode方法,如:obj.hasCode();
6.对同一个类的两个对象进行比较的equals()方法,如:obj1.eauqls(obj2);
7.生成字符串表示法的toString()方法,如:obj.toString().
什么是拆箱和装箱?
JAVA SE5之前是没有自动拆箱装箱的功能,想要生成一个基本的数据类型的包装类对象只能:
Integer i=new Integer(10);
而在JAVA SE5之后增添了自动拆箱和装箱的功能,想要生成数值为10的Integer对象,只需:
Integer i=10;
在这个过程中会自动根据数值创建相应的Integer对象,这就是装箱。
至于拆箱:
就是将包装类对象装换成基本的数据类型。
Integer i=10; //装箱
int j=i; //拆箱
简单来说,装箱就是将基本的数据类型转化成那的包装类对象,而拆箱就是将包装类对象转换成基本的数据类型。
拆箱和装箱是如何实现的?
以Integer包装类为例,在装箱的时候会自动调用Integer的valueOf(int)方法,在拆箱的时候自动调用Integer的intValue()方法。
总体来说,装箱功能是通过自动调用包装类的valueOf()方法实现的,拆箱功能是通过自动调用包装类的xxxValue()方法实现的。(xxx为数据类型)
装箱和拆箱可能出现的问题
知道了装箱功能是通过valueOf()方法实现的之后,我们去阅读一下Integer包装类的valueOf()方法。
/
valueOf()方法
/
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
/
IntegerCache类
/
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer’s autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
由上面的源代码可以看出来,当数值在[-128,127]之间时,便返回已经存在的对象的引用,否则创建一个新的Integer对象。
这就是为什么
Integer i=2;
Integer j=2;
System.out.print(i==j);
输入true的原因。
再看一下Double包装类的valueOf()方法的源码:
/
Double包装类的valueOf()源码
/
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s));
}
当Double包装类进行装箱的时候不管传入的数字是多少,都会new一个Double包装类的对象。也就是说虽然数值内容相等,然而使用“==”进行比较的时候,还是会返回false,这是因为‘==’比较的是地址。
接着看一下Boolean包装类的源码:
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
TRUE跟FALSE是两个静态属性。
public static final Boolean TRUE = new Boolean(true);
/
The Boolean
object corresponding to the primitive
value false
.
*/
public static final Boolean FALSE = new Boolean(false);
注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。Double、Float、String的valueOf方法的实现是类似的。
谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。
当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:
1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;
2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。
注意:
当 “==”运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。