随笔分类
装箱与拆箱
概述
数据类型 | 包 装 类 | 字节长度 | 默 认 值 |
---|---|---|---|
int | Integer | 4 | 0 |
short | Short | 2 | 0 |
long | Long | 8 | 0l或0L |
byte | Byte | 1 | 0 |
float | Float | 4 | 0.0F或0.0f |
double | Double | 8 | 0.0 |
char | Character | 2 | u0000 |
boolean | Boolean | 1 | false |
概述
自动装箱:实际上是对基本数据类型的一种封装,即意味着有对象的创建,将基本数据类型转为包装器类型.
自动拆箱:将包装器类型转为其对应的基本数据类型.
当进行大量自动装箱操作时,会有着大量对象的创建,即消耗了堆内存,影响性能.
这个过程时自动来执行
的.
结合 .Class 演示小案例:
Integer a = 10; //自动装箱 Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
int b = a; //自动拆箱 Method java/lang/Integer.intValue:()I
可见,装箱与Integer.valueof()
挂钩
拆箱与Integer.intValue()
挂钩.
Integer.intValue()
有源码可知,调用了intValue实质是就是对int类型的一种封装,最终都会有Integer类型的对象创建.
public static Integer valueOf(int i) { //自动装箱
if (i >= IntegerCache.low && i <= IntegerCache.high) //low : -128, high : 127
return IntegerCache.cache[i + (-IntegerCache.low)]; //可见,当 -128 <= i <= 127 时,直接从数组(i + (-128))里返回数据,这不也是共享模式的一种应用?
return new Integer(i); //i < -128 || i > 127 时直接新建一个Integer包装器对象.
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; //底层数组,也是Integer类型,默认大小 256
static { //静态代码块,在类加载时会被调用,主要用于类的初始化,且只能被执行一次.
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1]; //默认大小 256
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
来个小test
/**
* 对于整形,在某个范围内的整数是有限的
*/
Integer i = -128;
Integer i2 = -128;
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i == i2); //i, i2实际上拿到的是数组里统一对象 true -128 <= i <= 127
System.out.println(i3 == i4); // i3, i4 new Integer() false
Integer.intValue()
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() { //真相了,返回其包装的真实数据,即自动拆箱
return value;
}
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value; //包装器对象实际包装的数据
对于不同方法的valueOf()的具体实现可能是不同的,因为其基本数据类型的定义本身就是不同的
如int,对于整形数据而言,在某一范围内整数的个数是有限的,而对于double而言那就不一样了,在某一范围内浮点数的却是无限的
//Double.valueOf
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s)); //每次返回的实质上都是一个新的 Double对象,Float即同理
}
测试Demo
/**
* 对于Double,某范围内的浮点数是无限的,故其valueof具体实现可不同
*/
Double a = 100.0;
Double a2 = 100.0;
Double a3 = 200.0;
Double a4 = 200.0;
System.out.println(a == a2); //false
System.out.println(a3 == a4); //false
再来看看Boolean
boolean数据仅有true 和 false两种值,
即意味着其数据可以复用,典型的复用了.
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code true}.
*/
public static final Boolean TRUE = new Boolean(true); //典型的静态不可改变的最终值
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code false}.
*/
public static final Boolean FALSE = new Boolean(false);
测试Demo
Boolean s = true;
Boolean s2 = true;
System.out.println(s == s2); //true 二者引用地址指向的是同一个 static final Boolean对象.
总结
有限的数据实际上都会cache,另类的共享模式的落地实现.
自动执行
在涉及到封装数据类型的运算操作时,实质上会将其先转为其对应的基本数据类型再来进行相关操作
看个Demo
Integer i = 10;
int j = 5;
Integer k = i + j; //实质上先对 i 调用 intValue() 进行运算操作后再去调用Valueof 进行封装.
9: invokevirtual #3 // Method java/lang/Integer.intValue:()I
12: iload_2
13: iadd
14: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
实质上不仅限于运算符,一些比较的操作符也涉及了自动拆箱
int i = 10;
Integer j = 10;
System.out.println(i == j);//true
System.out.println(i == j);//true
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();//可见,在equals时若涉及了Integer的比较,会去调用 intValue()
}
return false;
}
其它的包装类型同理类似,有兴趣的自己去探索一下吧QAQ