马上就要到跳槽的“旺季”,即所谓的金三银四。由于奶昔年前就开始了面试求职之路,到今天也算是尘埃落地了,应该不打算再加入跳槽大军。「Java面试指南」这个系列的文章,主要是记录自己在面试中整理的知识点,以及对扩展知识的思考,希望能对你有所帮助。
面试中的回答没有什么标准答案,只要表达清楚其中的关键点就可以,所以文中的答案仅供参考。
今天的问题是:int和Integer的区别是什么?在往下看之前,我希望你能停下来思考一下:如果面试时面试官问你这么一个问题,你会怎么回答?
一、面试回答
int 是 Java 中常说的整型数字,是Java的8种原始数据类型之一,另外七种是:boolean、byte、short、char、float、long、double。Java中虽然一切都是对象,但是这八种数据类型是一个例外。
Integer 是 Java 中 int 类型的包装类,有一个int类型的类变量value 储存数据。并且提供了基本的数学运算、类型转换。
Java5中引入了自动装箱/拆箱,在编译阶段,会根据上下文自动进行转换。装箱会调用 Valueof(), 拆箱会调用 intValue。从 Java9 开始,Integer 的构造器被标记为废弃,官方文档要求使用 Integer 的静态工厂类 ValueOf 方法。
Integer 类的 ValueOf 方法中有一个缓存机制,会先对数值进行判断,如果数值在默认的[-128:127]之间,则返回缓存的对象,否则就返回一个新的 Integer 对象。上限127 可以通过JVM提供的配置(-XX:AutoBoxCacheMax)进行修改。
二、深入理解
一般来说,很少会有面试官会在面试的时候问到 int 和 Integer 的的区别,而有关缓存的考点更多的会出现在笔试题中,但是这并不代表我们不需要去关注和理解。就比如日常编码时,什么时候该用 int,什么时候该用 Integer,不知你是否真的清楚?
(一)、缓存机制
所谓的缓存机制,就是基本类型的包装类,为了提升效率以及节约内存而引入的机制,除了 Integer 类默认情况下缓存了[-128:127]之间的数值外,其他基本类型(除了 Float 和 Double)都有缓存对象。
- Boolean类中缓存了两个常量实例 Boolean.TRUE/FALSE
- Short 类固定缓存了-128到127之间的值
- Byte 因为表示的数值有限,所以全部缓存了
- Long 固定缓存了-128到127之间的值
- Character 缓存的范围是 ‘\u0000’ 到 ‘\u007F’
(二)、自动装箱/拆箱
谈及基本数据类型与包装类,就会很自然的聊到自动装箱和拆箱。这两种行为是Java
中提供的语法糖之一,所谓的语法糖,简单的理解就是 Java 平台为了方便我们进行程序开发,而在底层进行的一些转换操作,这种语法对语言本身功能而言并不会产生什么影响,转换的工作发生在编译阶段。
以 int 和 Integer 为例,日常编码时我们更习惯于直接将整数赋值给变量,而不是使用 Integer.Valueof() 方法,但是代码的运行结果是一致的
Integer demo = 1; // <=> Integer demo = Integer.valueOf(1)
int unboxing = demo; // <=> int unboxing = demo.intValue()
使用 javac 编译这两行代码后,再用 javap 查看编译后的 class 文件,就能看到:
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
8: invokevirtual #3 // Method java/lang/Integer.intValue:()I
(三)、基本类型的线程安全
由于JVM直接保证的原子性变量操作包括 read、write、assign、store、load,大致可以认为基本数据类型的读写是具备原子性(除了long 和 double,这两种基本类型的读写原子性依赖于虚拟机的具体实现,因为对于没有使用 volatile 修饰的64位的数据结构,Java虚拟机规范是允许分两次来操作的)
除了读写操作外,对基本类型的变量操作都是非线程安全的,需要利用相关的并发技术来保证线程安全,比如加锁或者使用 JUC 下的 Atomic 开头的线程安全类。
(四)、源码阅读
Integer 类从整体上来看,主要包括了用于表示最大值、最小值、位数等信息的常量,将各种其他类型转换为Integer对象的静态工厂方法 valueOf()以及将整数转换为其他基本类型的方法,如 byteValue()、shortValue()等等,还提供了进制转换的方法,如 toBinaryString() 用于将 整数转换为二进制字符串
1. Integer 源码
Intger 是不可变类,使用了 final 修饰 class,表明 Integer 无法被继承,使用 final 修饰的 value 属性来保存数字,即一旦Integer对象创建后,数值就不会再改变。
Integer 的最大值为 0x7fffffff,转换成十进制为 2^31-1,最小值为 0x80000000,转换成十进制是-2^31。使用常量 SIZE = 32 来表示 int 的位数,即32位。因为int 是有符号数,需要一位来表示正负号,所以剩下的31位用来表示实际的数值。
2. Integer的缓存机制
Inetger 缓存的对象保存在 Integer 的内部类 IntegerCache 中,static语句块中是缓存对象的生成逻辑。默认情况下,-128 ~ 127范围内 Integer 对象保存在 Integer 数组中。
static 语句块会在初始化阶段,由虚拟机执行<clinit>方法时执行。虚拟机会保证同一时间只会有一个线程执行<clinit>方法,所以是线程安全的。而 cache 数组对象是单例,也就是说 IntegerCache 采用了单例模式。
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); // 读取JVM的配置,也就是 -XX:AutoBoxCacheMax 指定的值
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;
}
三、小结
虽然面试中直接问到int和Integer的区别的几率并不大,而且也没有特别多可以展开细说的知识点,但是关于Integer的源码,建议还是要去阅读了解。最后,祝君面试顺利,薪资翻倍!
END