这篇文章绝对干货!文章涉及到的概念经常会被面试官拿来考察求职者的 Java 基础。
本篇采用大家比较喜欢的面试官问答的形式来展开。
基本数据类型
👨💻面试官 : Java 中有哪 8 种基本数据类型?
🙋 我 :Java 中有 8 种基本数据类型,分别为:
- 6 种数字类型 :
byte
、short
、int
、long
、float
、double
- 1 种字符类型:
char
- 1 种布尔型:
boolean
。
👨💻面试官 : 它们的默认值和占用的空间大小知道不?
🙋 我 :这 8 种基本数据类型的默认值以及所占空间的大小如下:
<table data-lake-id="6d2ce943" id="6d2ce943" margin="true" class="lake-table" style="width: 748px"><colgroup><col width="187"><col width="187"><col width="187"><col width="187"></colgroup><tbody><tr data-lake-id="u2bcc1a8e" id="u2bcc1a8e"><td data-lake-id="uedf83507" id="uedf83507"><p data-lake-id="u2cda54a1" id="u2cda54a1"><span data-lake-id="u07a27119" id="u07a27119">基本类型</span></p></td><td data-lake-id="ubd4b5ec1" id="ubd4b5ec1"><p data-lake-id="ud0a041c7" id="ud0a041c7"><span data-lake-id="u21df0745" id="u21df0745">位数</span></p></td><td data-lake-id="u1f1eac77" id="u1f1eac77"><p data-lake-id="u64a7a203" id="u64a7a203"><span data-lake-id="u1854aa6d" id="u1854aa6d">字节</span></p></td><td data-lake-id="u49c5830f" id="u49c5830f"><p data-lake-id="ud0b6da40" id="ud0b6da40"><span data-lake-id="u2c643d03" id="u2c643d03">默认值</span></p></td></tr><tr data-lake-id="uaab210c2" id="uaab210c2"><td data-lake-id="u39415dfb" id="u39415dfb"><p data-lake-id="u7d72d025" id="u7d72d025"><code data-lake-id="ue19a3cc3" id="ue19a3cc3"><span data-lake-id="uf0e157c6" id="uf0e157c6">int</span></code></p></td><td data-lake-id="ubc478417" id="ubc478417"><p data-lake-id="u223657ab" id="u223657ab"><span data-lake-id="udeefba1f" id="udeefba1f">32</span></p></td><td data-lake-id="u7da3ceb5" id="u7da3ceb5"><p data-lake-id="u16b27220" id="u16b27220"><span data-lake-id="uffe984b9" id="uffe984b9">4</span></p></td><td data-lake-id="u6c2faa6a" id="u6c2faa6a"><p data-lake-id="u34c2e746" id="u34c2e746"><span data-lake-id="u67c90715" id="u67c90715">0</span></p></td></tr><tr data-lake-id="u33b7b051" id="u33b7b051"><td data-lake-id="u4995d369" id="u4995d369"><p data-lake-id="u06ab249c" id="u06ab249c"><code data-lake-id="ud40eeb28" id="ud40eeb28"><span data-lake-id="ue68c9e35" id="ue68c9e35">short</span></code></p></td><td data-lake-id="u61ac57ff" id="u61ac57ff"><p data-lake-id="u10158178" id="u10158178"><span data-lake-id="u3819c003" id="u3819c003">16</span></p></td><td data-lake-id="u04fb11bd" id="u04fb11bd"><p data-lake-id="u9f400fb5" id="u9f400fb5"><span data-lake-id="ub690c42d" id="ub690c42d">2</span></p></td><td data-lake-id="u7782e4bd" id="u7782e4bd"><p data-lake-id="ud30a5c99" id="ud30a5c99"><span data-lake-id="u4825bb05" id="u4825bb05">0</span></p></td></tr><tr data-lake-id="u504ae640" id="u504ae640"><td data-lake-id="u1badc7f9" id="u1badc7f9"><p data-lake-id="uacbd7aa8" id="uacbd7aa8"><code data-lake-id="u73ba6107" id="u73ba6107"><span data-lake-id="u06eb0754" id="u06eb0754">long</span></code></p></td><td data-lake-id="u8e36544c" id="u8e36544c"><p data-lake-id="u7bfee2e6" id="u7bfee2e6"><span data-lake-id="u4910f3a9" id="u4910f3a9">64</span></p></td><td data-lake-id="u203d4786" id="u203d4786"><p data-lake-id="u6a5e0170" id="u6a5e0170"><span data-lake-id="u6be31303" id="u6be31303">8</span></p></td><td data-lake-id="u149dd781" id="u149dd781"><p data-lake-id="u8f00dfbe" id="u8f00dfbe"><span data-lake-id="ufda4ab6e" id="ufda4ab6e">0L</span></p></td></tr><tr data-lake-id="u5212fa34" id="u5212fa34"><td data-lake-id="u65166db9" id="u65166db9"><p data-lake-id="u00c207d3" id="u00c207d3"><code data-lake-id="ub8a8dc6e" id="ub8a8dc6e"><span data-lake-id="ub3a0b5c3" id="ub3a0b5c3">byte</span></code></p></td><td data-lake-id="uf42d03e0" id="uf42d03e0"><p data-lake-id="u237cf3ea" id="u237cf3ea"><span data-lake-id="uad19ec0e" id="uad19ec0e">8</span></p></td><td data-lake-id="u1f23e965" id="u1f23e965"><p data-lake-id="u60ef6976" id="u60ef6976"><span data-lake-id="ue711ef27" id="ue711ef27">1</span></p></td><td data-lake-id="u5164ef33" id="u5164ef33"><p data-lake-id="u3628ff0c" id="u3628ff0c"><span data-lake-id="u830fecce" id="u830fecce">0</span></p></td></tr><tr data-lake-id="uf2cdf0ef" id="uf2cdf0ef"><td data-lake-id="u859e68f5" id="u859e68f5"><p data-lake-id="u2eca88e1" id="u2eca88e1"><code data-lake-id="uabb61a4f" id="uabb61a4f"><span data-lake-id="ua2680076" id="ua2680076">char</span></code></p></td><td data-lake-id="ua6422744" id="ua6422744"><p data-lake-id="u8c10a2fe" id="u8c10a2fe"><span data-lake-id="ub78f7e3d" id="ub78f7e3d">16</span></p></td><td data-lake-id="ud37c254d" id="ud37c254d"><p data-lake-id="u9b47f80f" id="u9b47f80f"><span data-lake-id="u01b42dcd" id="u01b42dcd">2</span></p></td><td data-lake-id="u86fd071d" id="u86fd071d"><p data-lake-id="u23212a39" id="u23212a39"><span data-lake-id="udeba0f58" id="udeba0f58">'u0000'</span></p></td></tr><tr data-lake-id="uabf8a637" id="uabf8a637"><td data-lake-id="u72992057" id="u72992057"><p data-lake-id="uec442c1c" id="uec442c1c"><code data-lake-id="u59a4104b" id="u59a4104b"><span data-lake-id="u928b6243" id="u928b6243">float</span></code></p></td><td data-lake-id="ua4bbdfb5" id="ua4bbdfb5"><p data-lake-id="u30b2fde1" id="u30b2fde1"><span data-lake-id="u9635ae78" id="u9635ae78">32</span></p></td><td data-lake-id="ud649e9bd" id="ud649e9bd"><p data-lake-id="uf16a2789" id="uf16a2789"><span data-lake-id="ued0a45bd" id="ued0a45bd">4</span></p></td><td data-lake-id="uf54d389e" id="uf54d389e"><p data-lake-id="u031d2613" id="u031d2613"><span data-lake-id="ue09b9739" id="ue09b9739">0f</span></p></td></tr><tr data-lake-id="u3ad405b2" id="u3ad405b2"><td data-lake-id="u2e16e479" id="u2e16e479"><p data-lake-id="u8fc46f92" id="u8fc46f92"><code data-lake-id="u815eac46" id="u815eac46"><span data-lake-id="u980be52f" id="u980be52f">double</span></code></p></td><td data-lake-id="u297e26d0" id="u297e26d0"><p data-lake-id="u2bb94fa8" id="u2bb94fa8"><span data-lake-id="ucefbdaf0" id="ucefbdaf0">64</span></p></td><td data-lake-id="u8da5520e" id="u8da5520e"><p data-lake-id="u1f1168ff" id="u1f1168ff"><span data-lake-id="u3fc19de4" id="u3fc19de4">8</span></p></td><td data-lake-id="ue00cd512" id="ue00cd512"><p data-lake-id="u3eff2be0" id="u3eff2be0"><span data-lake-id="ud3a78c21" id="ud3a78c21">0d</span></p></td></tr><tr data-lake-id="u936096fa" id="u936096fa"><td data-lake-id="u8a4aed80" id="u8a4aed80"><p data-lake-id="u4e79a81c" id="u4e79a81c"><code data-lake-id="uf2e7af37" id="uf2e7af37"><span data-lake-id="uae6f1ee3" id="uae6f1ee3">boolean</span></code></p></td><td data-lake-id="u1866fcda" id="u1866fcda"><p data-lake-id="u57f885d1" id="u57f885d1"><span data-lake-id="ube27a548" id="ube27a548">1</span></p></td><td data-lake-id="uc439aeeb" id="uc439aeeb"></td><td data-lake-id="u95b10ffd" id="u95b10ffd"><p data-lake-id="u53782c1c" id="u53782c1c"><span data-lake-id="u1a8271f5" id="u1a8271f5">false</span></p></td></tr></tbody></table>
另外,对于 boolean
,官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1 位,但是实际中会考虑计算机高效存储因素。
注意:
- Java 里使用
long
类型的数据一定要在数值后面加上 L,否则将作为整型解析: char a = 'h'
char :单引号,String a = "hello"
:双引号
包装类型
👨💻面试官 : 说说这 8 种基本数据类型对应的包装类型。
🙋 我 :这八种基本类型都有对应的包装类分别为:Byte
、Short
、Integer
、Long
、Float
、Double
、Character
、Boolean
👨💻面试官 :那基本类型和包装类型有啥区别不?
🙋 我 :包装类型不赋值就是 Null
,而基本类型有默认值且不是 Null
。
另外,这个问题建议还可以先从 JVM 层面来分析。
基本数据类型直接存放在 Java 虚拟机栈中的局部变量表中,而包装类型属于对象类型,我们知道对象实例都存在于堆中。相比于对象类型, 基本数据类型占用的空间非常小。
《深入理解 Java 虚拟机》 :局部变量表主要存放了编译期可知的基本数据类型**(boolean、byte、char、short、int、float、long、double)**、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
包装类型的常量池技术
👨💻面试官 : 包装类型的常量池技术了解么?
🙋 我 : Java 基本类型的包装类的大部分都实现了常量池技术。
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在[0,127]范围的缓存数据,Boolean
直接返回 True
Or False
。
Integer 缓存源码:
**Character**
缓存源码:
**Boolean**
缓存源码:
如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
两种浮点数类型的包装类 Float
,Double
并没有实现常量池技术。
下面我们来看一下问题。下面的代码的输出结果是 true
还是 flase
呢?
Integer i1=40
这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40)
。因此,i1
直接使用的是常量池中的对象。而Integer i1 = new Integer(40)
会直接创建新的对象。
因此,答案是 false
。你答对了吗?
记住:所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
为什么要有包装类型?
👨💻面试官 : 为什么要有包装类型?
🙋 我 :
Java 本身就是一门 OOP(面向对象编程)语言,对象可以说是 Java 的灵魂。
除了定义一些常量和局部变量之外,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。
为什么呢?
我举个例子,假如你有一个对象中的属性使用了 基本类型,那这个属性就必然存在默认值了。这个逻辑不正确的!因为很多业务场景下,对象的某些属性没有赋值,我就希望它的值为 null。你给我默认赋个值,不是帮倒忙么?
另外,像泛型参数不能是基本类型。因为基本类型不是 Object
子类,应该用基本类型对应的包装类型代替。我们直接拿 JDK 中线程的代码举例。
Java 中的集合在定义类型的时候不能使用基本类型的。比如:
自动拆装箱
什么是自动拆装箱?原理?
👨💻面试官 : 什么是自动拆装箱?原理了解么?
🙋 我 :
基本类型和包装类型之间的互转。举例:
上面这两行代码对应的字节码为:
从字节码中,我们发现装箱其实就是调用了 包装类的valueOf()
方法,拆箱其实就是调用了 xxxValue()
方法。
因此,
Integer i = 10
等价于Integer i = Integer.valueOf(10)
int n = i
等价于int n = i.intValue()
;
自动拆箱引发的 NPE 问题
👨💻面试官 : 自动拆箱可能会引发 NPE 问题,遇到过类似的场景么?
🙋 我 :
案例 1
在《阿里巴巴开发手册》上就有这样一条规定。
我们从上图可以看到,有一条是这样说的:“数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险”。
我们来模拟一个实际的案例:
运行代码之后,果然出现了 NPE 的问题。
为什么会这样呢? 我们对 AutoBoxTest.class
进行反编译查看其字节码(我更推荐使用 IDEA 插件 jclasslib 来查看类的字节码)。
反编译后得到的 should_Throw_NullPointerException()
方法的字节码如下:
我们可以发现自动拆箱 Long
-> long
的过程,不过是调用了 longValue()
方法罢了!
也就是说下面两行的代码实际是等价的:
因为,getNum()
返回的值为 null
,一个 null
值调用方法,当然会有 NPE 的问题了。
案例 2
通过上面的分析之后,我来考了一个不论是平时开发还是面试中都经常会碰到的一个问题:“三目运算符使用不当会导致诡异的 NPE 异常”。
请你回答下面的代码会有 NPE 问题出现吗?如果有 NPE 问题出现的话,原因是什么呢?你会怎么分析呢?
答案是会有 NPE 问题出现的。
我们还是通过查看其字节码来搞懂背后的原理(这里借助了 IDEA 插件 jclasslib 来查看类字节码)。
从字节码中可以看出,22 行的位置发生了 拆箱操作 。
详细解释下就是:flag ? 0 : i
这行代码中,0 是基本数据类型 int,返回数据的时候 i 会被强制拆箱成 int 类型,由于 i 的值是 null,因此就抛出了 NPE 异常。
如果,我们把代码中 flag
变量的值修改为 true 的话,就不会存在 NPE 问题了,因为会直接返回 0,不会进行拆箱操作。
我们在实际项目中应该避免这样的写法,正确 ✅ 修改之后的代码如下:
这个问题也在 《阿里巴巴开发手册》中 被提到过。