JAVA 自动拆装箱详解
1.自动拆装箱子的作用和意义
Java语言是一种面向对象的语言,但是Java中的基本数据类型不是面向对象的,不能之间参与面向对象的开发,为了解决这个不足,java 提供了8个基本类型对应的包装类,这样就可以参与面向对象的开发了
基本类型和装箱类型对照:
基本类型 | 装箱类型 |
---|---|
int (4字节) | Integer |
byte (1字节) | Byte |
double (8字节 | Double |
short (2字节) | Short |
long (8字节) | Long |
char(2字节) | Char |
float (4字节) | Float |
boolean (未定) | Boolean |
2.什么是自动拆装箱
1) 装箱、拆箱:把基本类型转化为对应的包装类,叫做装箱,反之,把包装类转换成基本类型叫做拆箱
其中,6个数字类型的包装类继承自java.lang.Number,Number是一个抽象类,定义了可以让数字类型之间 进行互转的方法。
例如:
int a = 5;
Integer a1 = Integer.valueOf(a);//装箱,把int 变量转化为 Integer 对象
int b = a1.intValue();//拆箱,把 Integer 对象a1 转化为 基本类型 b
2)数字类型的包装类都有两个常量:MAX_VALUE,MIN_VALUE,用于表示其对应的基本类型数据的取值范围
例如:
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
long lmax = Long.MAX_VALUE;
3)包装类支持一个静态方法:parseXXX(String str),可以将给定的字符串解析为对应的基本类型,但是前提是该字符串内容必须正确表示基本类型可以保存的值。
例如:
String str = "123";
int d = Integer.parseInt(str);
double dou = Double.parseDouble(str);
自动拆箱,装箱:DK1.5之后推出了一个特性:自动拆装箱。该特性是编译器认可,而非JVM认可。编译器在编译源代码时会自动补全基本类型与包装类之间的转换代码完成相互转换工作,从而让我们在编写源代码时实现了基本类型与包装类之间相互赋值的操作
例如:
/*
* 触发了编译器自动拆箱特性
* 编译器会自动补全代码完成包装类转换为
* 基本类型的操作,下面代码在class文件
* 中会被改为:
* int d = new Integer(1).intValue();
*/
int d = new Integer(1);
/*
* 触发了编译器自动装箱特性
* 编译器会自动补全代码完成基本类型转换为
* 包装类的操作,下面代码在class文件
* 中会被改为:
* Integer in = Integer.valueOf(1);
*/
Integer in = 1;
3.自动拆装箱使用的注意事项
例如:
Integer a = 1;
Integer b = 1;
Integer c = 144;
Integer d = 144;
Integer a1 = new Integer(1);
Integer b1 = new Integer(1);
System.out.println(a == b); //true
System.out.println(a.equals(b)); //true
System.out.println(a1 == b1); //false
System.out.println(a1.equals(b1)); //true
System.out.println(c == d); //false
System.out.println(c.equals(d)); //true
第7行为true而第12行为false呢?这是因为:
JVM会自动维护八种基本类型的常量池,int常量池中初始化的范围是 -128~127,所以当为Integer i=127时,在自动装箱过程中是取自常量池中的数值,而当Integer i=128时已经超过了int的范围,128不在常量池范围内,所以在自动装箱过程中需new 128,所以地址不一样
4.源码剖析
看一下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);
}
其中 ,IntegerCache.low和 IntegerCache.high的值参考下面代码
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
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) {
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);
}
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对象后,基本类型的常量池中被重用。所以在调用valueOf()方法的时候,会判断你所给的数是不是在IntegerCache.low 和 i <= IntegerCache.high之间,如果是,则从常量池中取数据,而不在这个范围里的数,你每次所生成的对象都是不同的。
自动装箱池的大小是怎么定义的呢,Integer.java中有这样一个内部类
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
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];
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;
}
private IntegerCache() {}
}
IntegerCache类,定义了Integer自动装箱池的大小。从源码中我们可以看到,下界是写死的(-128),但是上界却是由参数integerCacheHighPropValue 解析的,说明我们可以通过改变integerCacheHighPropValue值的大小来自定义自动装箱池的上限大小。