前几天同事遇到了一个很奇怪的 NullPointExption 异常,看她郁闷的蛮久都没有解决,也帮忙分析了一下,情况大致如下:
看得她一头雾水,在帮其初步分析也没想到是啥问题,因为是批量代码,没有相关文件不好本地测试,让其远程 debug 跟了一下代码,发现还是报空指针异常,最后怀疑是服务器上 jar 包没有更新到,拿下 jar 包反编译后,才恍然大悟(反编译如下图),是自己忽略 Person 类和 Student 类的类型了,一个是 Integer ,一个是 Int 基本类型,1.5 之后的 JDK 在编译的时候会有自动拆装箱机制!这里记录一下 Java 中包装类型相关的知识点,做个小总结。
基本类型和包装类
Java 中 8 种基本类型对应的包装类型如下表:
基本类型 | 大小 | 包装类型 |
---|---|---|
boolean | 1 Byte | Boolean |
char | 2 Byte | Character |
byte | 1 Byte | Byte |
short | 2 Byte | Short |
int | 4 Byte | Integer |
long | 8 Byte | Long |
float | 4 Byte | Float |
double | 8 Byte | Double |
自动装箱和拆箱
以 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 内部类 IntegerCache 里cache 数组缓存下来的一个 Integer 对象,不是在这个范围外,才会返回一个新的 Integer 对象。IntegerCache 内部类如下图:
IntegerCache 这个内部类,有个静态 Integer 类型 cache 数组,静态代码块会给 cache 数组初始化值,缓存符合要求的 Integer 值。这里也就引发了一个初级面试中经常会遇到的问题,如下图:
首先 == 在 Java 里,基本类型比较的是值是否相等,而引用型类型则是比较内存地址是否相同, Integer 是个引用类型,默认情况下,-128~127 范围内的 int 值在自动装箱时调用 valueOf(int)方法返回的都是 cache 里缓存好的对象,所以不管是 i ,还是 i2 引用,其实都是指向缓存数组 cache 早已经定义好的 Integer 对象,所以 i == i2 是 true ;而 128 不在 -128~127 的范围内,所以 valueOf(int) 返回的是 new Integer(int),返回一个新的 Integer 对象,所以 i3 == i4 也就是 false 了。
还有另外一种情况,代码截图如下:
这里的 i3 引用其实就是和自动装箱无关了,它就是用 Integer 的构造方法生成了一个新的 Integer 对象,和 cache 数组缓存的又是不同的对象,i2 == i3 也就等于 false 了。
注:除 Integer 包装类外,Byte、Short、Long、Character(char)包装类都是有类似于 Integer 的缓存机制的,Float、Double 由于是浮点类型的,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().
引用文章1
网友评论