今天在代码中看到了一个关于包装类型的运算,当时看到这个会引起bug,果然查看日志就是引起了bug。在说这个bug之前我们先看下java的包装了类把:
基本数据类型与包装类型对应如下表
byte | Byte |
---|---|
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
今日子在代码看到使用包装类型的做加减乘除运算,具体代码如下:
这里的是采用的domainCanUse对象是一个map形式的是
Map
在后面是通过get的方式进获取到然后转为为int类型的
int count=domainCanUse.get(“key”) // 这里调用了X.intValue()
而这里的key对应的value是null,所以把一个null的对象赋值给基本类型的int时会出出现错误。即使java有自动拆箱的功能,但是对于一个数字1,22,3这些的是java中的Integer对象调用的intValue()方法进行自动拆箱.不会出现任何的异常。但是对于Integer a=new Integer();这是初始化对象为null,调用intValue拆箱时就会报空指针异常。所以对上图下面的运算会出现异常。
Integer x = 2; // 装箱 调用了 Integer.valueOf(2)
int y = x; // 拆箱 调用了 X.intValue()
来看看下面的测试就明了了
结论:对于包装类型的使用,如果要做运算一定要判断是否为null,判断后java自动拆箱和自动装箱时才不会出现异常。对于包装类的其他方式将会慢慢介绍(好像还有一些缓存区):在8种包装类型中,有缓存区的有Character、Byte、Short、Integer、Long,而且它们的实现方式基本一样,都是-128到127的缓存范围。Boolean虽然没有缓存区,但是因为只有两个值true、false,所以Boolean在成员变量中就创建了两个相应的对象。没有缓存区的只有Float、Double,之所以没有原因很简单,即便是0到1这么小的范围,浮点数也有无数个,使用缓存区缓存它们不具备可能性和实用性。
下面看下关于包装类型的缓存区:
缓存区的存在使得常用的包装类对象可以得到复用,这有利于提升性能。当我们需要创建新对象的时候再new一个,增加了灵活性。
这里以Integer包装类做以下说明:
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
new Integer(123) 每次都会新建一个对象;
Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
// new 肯定会每次出创建一个不同的引用对象
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
看下valueOf()方法 就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容:Integer.valueOf(123);所以当多次调用时会取得同一个对象引用。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
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() {}
}
可以发现,只要Integer类第一次被使用到,Integer的静态内部类就被加载,加载的时候会创建-128到127的Integer对象,同时创建一个数组cache来缓存这些对象。当使用valueOf()方法创建对象时,就直接返回已经缓存的对象,也就是说不会再新建对象;当使用new关键字或使用valueOf()方法创建小于-128大于127的值对象时,就会创建新对象。
在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax= 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界。所以integerCache中初始化integerCacheHighPropValue是通过sun.misc.VM.getSavedProperty获取这个设定的参数进行配置的。
基本类型对应的缓冲池如下:
boolean values true and false
all byte values
short values between -128 and 127
int values between -128 and 127
char in the range \u0000 to \u007F
以上便是对包装类型引发的一个bug的探索,以及对integer对象的回顾。
参考:
https://blog.csdn.net/sinat_30973431/article/details/89332443
CS-Notes