基本类型
在Java中,有8种基本类型:int、double、float、boolean、long、short、byte、char,每种类型所占用的字节数如下:
类型 | short | int | long | float | double | boolean | byte | char |
---|---|---|---|---|---|---|---|---|
占用字节数 | 2个字节 | 4个字节 | 8个字节 | 4个字节 | 8个字节 | 1个字节 | 1个字节 | 1个字节 |
包装类型介绍
在Java中,每种基本类型都有对应的包装类型,比如int类型对应Integer包装类。包装类中除了包含原始类型的数据外,还增加了很多对数据操作的方法,比如parseInt(String s)、valueOf(int i)等,方便了对基本类型的操作。
包装类都是不可变类,一旦创建,就不能更改。
装箱和拆箱
装箱和拆箱操作其实是Java的语法糖,在编译期就会解除糖衣,以下面unBoxing()方法为例,在执行i1+i2操作时,其实编译器在背后先后做了拆箱和装箱操作,首先i1和i2对象分别调用Integer.intValue()进行拆箱,拆箱后的数值相加,得到结果后调用Integer.valueOf(int i)进行装箱,如下面反编译的结果所示。
public void unBoxing() {
Integer i1 = new Integer(1);
Integer i2 = new Integer(2);
//i1和i2会先拆箱,相加后再装箱
Integer i3 = i1 + i2;
}
对基本类型的相加不会经过复杂的装箱和拆箱操作,如下面示例所示:
public void primitiveType() {
int i1 = 1;
int i2 = 2;
int i3 = i1 + i2;
}
包装类型缓存
在通过装箱操作(Integer.valueOf())创建Integer对象时,JLS规定[-128,127]之间的整数进行缓存,如下面代码注释所描述,如果不需要创建一个新的对象,那么使用装箱操作创建对象可以获得更好的性能,原因是该方法会缓存经常使用的整数值。
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
缓存实现的原理比较简单,就是在第一次调用Integer.valueOf()时,会对预设的整数区间进行缓存,默认是[128,127],也可以通过 -XX:AutoBoxCacheMax=
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() {}
}
下面是几个示例,通过上面的分析,结果也都显而易见,唯一需要注意一点的是,包装类型的比较都需要用equals()。
public void cache() {
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
System.out.println("==:" + (i1 == i2) + " equals:" + i1.equals(i2));//false,true
Integer i3 = new Integer(101);
Integer i4 = Integer.valueOf(101);
System.out.println("==:" + (i3 == i4) + " equals:" + i3.equals(i4));//false,true
Integer i5 = 102;
Integer i6 = 102;
System.out.println("==:" + (i5 == i6) + " equals:" + i5.equals(i6));//true,true
Integer i7 = 129;
Integer i8 = 129;
System.out.println("==:" + (i7 == i8) + " equals:" + i7.equals(i8));//false,true
}
常犯的问题
case1:包装类型设值未判空
两个模型中字段的类型分别为包装类型和基本类型,在进行模型转换时,没有对包装类型的字段进行判空处理,直接设值,会出现空指针的问题,如下面示例:
Integer s = null;
int s1 = s.intValue();
case2:对外接口如何选择
对外暴露的接口,如果是数字类型,往往会在int/Integer、long/Long、double/Double之间做选择,以int/Integer为例,来看看它们之间的异同点。
首先它们都表示整形数字,数字的范围是相同的。int类型的默认值是0,Integer类型的默认值是null。如果0是有业务含义的,那么需要使用Integer,因为无法判断是外部系统主动设置的参数为0还是使用的默认值,多少有些模棱两可。