Java:基本类型和包装类

前几天同事遇到了一个很奇怪的 NullPointExption 异常,看她郁闷的蛮久都没有解决,也帮忙分析了一下,情况大致如下:

文章配图

看得她一头雾水,在帮其初步分析也没想到是啥问题,因为是批量代码,没有相关文件不好本地测试,让其远程 debug 跟了一下代码,发现还是报空指针异常,最后怀疑是服务器上 jar 包没有更新到,拿下 jar 包反编译后,才恍然大悟(反编译如下图),是自己忽略 Person 类和 Student 类的类型了,一个是 Integer ,一个是 int 基本类型,1.5 之后的 JDK 在编译的时候会有自动拆装箱机制!这里记录一下 Java 中包装类型相关的知识点,做个小总结。

文章配图

基本类型和包装类

Java 中 8 种基本类型对应的包装类型如下表:

基本类型大小包装类型
boolean1 ByteBoolean
char2 ByteCharacter
byte1 ByteByte
short2 ByteShort
int4 ByteInteger
long8 ByteLong
float4 ByteFloat
double8 ByteDouble

自动装箱和拆箱

Integer 包装类为例(其他的包装类也是类似),看看其自动拆装箱是如何实现的,写段 代码如下图:

自动装箱和拆箱

由上图可知,自动装箱时,JDK 在编译的时候会调用 Integer.valueOf(int) 这个方法将 int 转为 Integer,而在自动拆箱的时候 Integer 对象会调用 intValue() 方法(Short 类则为 shortValue()),返回 Integer 所持有的 int 成员变量,intValue() 没什么好看的,就是返回了 Integer 对象所持有的 int 成员变量。valueOf(int) 函数倒是值得研究一下,截图如下:

自动装箱和拆箱

可知在 int 值在 -127~ IntegerCache.high(VM 参数 -Djava.lang.Integer.IntegerCache.high=*** 可以设置 high 的值)之间时,将会返回一个Integer 内部类 IntegerCachecache 数组缓存下来的一个 Integer 对象,不是在这个范围外,才会返回一个新的 Integer 对象。IntegerCache 内部类如下图:

自动装箱和拆箱

IntegerCache 这个内部类,有个静态 Integer 类型 cache 数组,静态代码块会给 cache 数组初始化值,缓存符合要求的 Integer 值。这里也就引发了一个初级面试中经常会遇到的问题,如下图:

自动装箱和拆箱

首先 == 在 Java 里,基本类型比较的是值是否相等,而引用型类型则是比较内存地址是否相同, Integer 是个引用类型,默认情况下,-128到127 范围内的 int 值在自动装箱时调用 valueOf(int)方法返回的都是 cache 里缓存好的对象,所以不管是 i ,还是 i2 引用,其实都是指向缓存数组 cache 早已经定义好的 Integer 对象,所以 i == i2true ;而 128 不在 -128到127 的范围内,所以 valueOf(int) 返回的是 new Integer(int),返回一个新的 Integer 对象,所以 i3 == i4 也就是 false 了。

还有另外一种情况,代码截图如下:

自动装箱和拆箱

这里的 i3 引用其实就是和自动装箱无关了,它就是用 Integer 的构造方法生成了一个新的 Integer 对象,和 cache 数组缓存的又是不同的对象,i2 == i3 也就等于 false 了。

:除 Integer 包装类外,ByteShortLongCharacterchar)包装类都是有类似于 Integer 的缓存机制的,FloatDouble 由于是浮点类型的,JDK 中并未实现这种缓存机制。至于 Boolean 类型的的话又是一个例外,翻看它的实现大致就可以知道啥原理了,其实现如下:

自动装箱和拆箱

其中包装类中拥有共同的方法如下:

带有基本值参数并创建包装类对象的构造函数。如利用Integer包装类创建对象,Integer obj=new Integer(145);
带有字符串参数并创建包装类对象的构造函数.如:new Integer("-45.36");
可生成对象基本值的typeValue方法,如:obj.intValue();
将字符串转换为基本值的parseType方法,如:Integer.parseInt(args[0]);
生成哈稀表代码的hashCode方法,如:obj.hasCode();
对同一个类的两个对象进行比较的equals()方法,如:obj1.eauqls(obj2);
生成字符串表示法的toString()方法,如:obj.toString().