Java 基本数据类型高频面试题
一、基础概念类
1. Java 有哪八种基本数据类型?各自的位数、字节、取值范围是什么?
核心回答 :
Java 有 8 种基本数据类型,分为 4 类:
| 类型 | 位数(bit) | 字节(byte) | 默认值 | 取值范围 | 包装类 |
|---|---|---|---|---|---|
| byte(字节型) | 8 | 1 | 0 | -128 ~ 127 | Byte |
| short(短整型) | 16 | 2 | 0 | -32768 ~ 32767 | Short |
| int(整型) | 32 | 4 | 0 | -2¹⁰⁷³⁷⁴¹⁸²⁴ ~ 2¹⁰⁷³⁷⁴¹⁸²³ | Integer |
| long(长整型) | 64 | 8 | 0L | -9223372036854775808 ~ 9223372036854775807 | Long |
| float(单精度浮点型) | 32 | 4 | 0.0f | 约 ±3.40282347E+38F(有效位数6~7位) | Float |
| double(双精度浮点型) | 64 | 8 | 0.0d | 约 ±1.79769313486231570E+308(有效位数15~16位) | Double |
| char(字符型) | 16 | 2 | '\u0000'(空字符) | \u0000 ~ \uffff(0~65535) | Character |
| boolean(布尔型) | 1(JVM 规范未明确,通常1字节) | 1 | false | true / false | Boolean |
补充考点:
- 为什么
char是 16 位?因为 Java 采用 Unicode 编码,16 位可以覆盖 BMP(基本多语言平面)的所有字符,支持全球语言。 boolean在 JVM 中没有明确的大小规范,HotSpot 虚拟机中用 1 字节存储,数组中用 1 位/元素压缩存储。
2. int 和 long 是多少位、多少字节?long 和 int 可以互转吗?
(1)位数/字节
int:32 位,4 字节,取值范围约 ±21 亿long:64 位,8 字节,取值范围约 ±900 亿亿
(2)能否互转?
可以,但分两种情况:
-
int → long(自动类型转换,安全无风险)
因为 long 取值范围完全包含 int,编译器会自动完成转换,不会丢失精度。javaint a = 100; long b = a; // 自动转换,等价于 long b = (long)a; -
long → int(强制类型转换,有风险)
因为 int 范围小于 long,必须手动强转,如果 long 值超出 int 范围,会直接截断高位,导致数值错误 。javalong a = 100L; int b = (int)a; // 正常,结果100 long c = 3000000000L; // 超出int最大值2147483647 int d = (int)c; // 强转后结果为-1294967296,完全错误!
3. 数据类型转换方式有哪些?
核心回答 :分为自动类型转换(隐式)和 强制类型转换(显式)两类,还有字符串与基本类型的转换。
(1)自动类型转换(隐式)
-
规则:小范围类型 → 大范围类型,编译器自动完成,无精度损失。
-
转换顺序 :
byte/short/char → int → long → float → double注意:byte、short、char三者之间不会自动转换,运算时会先提升为int -
示例 :
javabyte a = 10; int b = a; // byte→int,自动转换 double c = b; // int→double,自动转换
(2)强制类型转换(显式)
-
规则 :大范围类型 → 小范围类型,必须手动加
(类型),可能丢失精度/数值错误。 -
示例 :
javadouble a = 3.14; int b = (int)a; // 强制转换,结果为3,丢失小数部分 long c = 100L; short d = (short)c; // 强制转换,结果100
(3)字符串与基本类型的转换
-
基本类型 → 字符串 :
String.valueOf(xxx)或"" + xxxjavaint a = 100; String s1 = String.valueOf(a); // "100" String s2 = a + ""; // "100" -
字符串 → 基本类型 :包装类的
parseXxx()方法javaString s = "123"; int a = Integer.parseInt(s); // 123 double b = Double.parseDouble("3.14"); // 3.14
4. 类型互转会出现什么问题?
核心回答:主要有 3 类问题:
-
精度丢失 :浮点型转整型时,直接截断小数部分,不四舍五入
javadouble a = 3.99; int b = (int)a; // 结果3,不是4 -
数值溢出/错误 :大范围整型转小范围整型时,高位被截断,结果完全错误
javalong a = 3000000000L; int b = (int)a; // 结果-1294967296,完全偏离原值 -
符号位问题 :有符号数转无符号数(如
byte转char)时,符号位被当作数值位,导致数值异常javabyte a = -1; // 二进制11111111 char b = (char)a; // 结果为\uffff(65535),完全不是-1 -
浮点数精度丢失(高频考点) :
float/double是二进制浮点数,无法精确表示 0.1 这类十进制小数,运算会出现误差javaSystem.out.println(0.1 + 0.2); // 输出0.30000000000000004,不是0.3
5. 为什么用 BigDecimal 不用 double?
核心回答 :
double 是二进制浮点数 ,基于 IEEE 754 标准,只能近似表示十进制小数,无法精确存储 0.1、0.2 这类数,在金融、金额计算等高精度场景会出现严重误差 。
BigDecimal 是十进制浮点数,可以精确表示任意十进制小数,完全避免精度丢失,是金额计算的唯一正确选择。
示例对比:
java
// double 计算误差
double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 0.30000000000000004,错误
// BigDecimal 精确计算
BigDecimal c = new BigDecimal("0.1");
BigDecimal d = new BigDecimal("0.2");
System.out.println(c.add(d)); // 0.3,正确
补充避坑点:
- 不要用
new BigDecimal(double)构造,会把 double 的误差带入 BigDecimal,必须用new BigDecimal("字符串")或BigDecimal.valueOf(double) - 除法必须指定舍入模式(如
RoundingMode.HALF_UP四舍五入),否则除不尽会抛异常
二、装箱拆箱与包装类
6. 装箱和拆箱是什么?
核心回答:
- 装箱(Boxing) :把基本数据类型 自动转换为对应的包装类对象 (如
int → Integer) - 拆箱(Unboxing) :把包装类对象 自动转换为对应的基本数据类型 (如
Integer → int) - 这是 Java 5 引入的语法糖,由编译器自动完成,本质是调用包装类的
valueOf()(装箱)和xxxValue()(拆箱)方法。
示例:
java
// 手动装箱/拆箱(Java 5 之前)
Integer a = Integer.valueOf(10); // 手动装箱
int b = a.intValue(); // 手动拆箱
// 自动装箱/拆箱(Java 5+)
Integer c = 10; // 自动装箱,等价于Integer.valueOf(10)
int d = c; // 自动拆箱,等价于c.intValue()
7. Java 为什么要有 Integer?Integer 相比 int 有什么优点?为什么还要保留 int?
(1)为什么要有 Integer?
Java 是面向对象语言,基本数据类型不是对象,无法满足面向对象的需求(如泛型、集合、反射、空值表示),因此为每种基本类型设计了对应的包装类,让基本类型可以以对象的形式存在。
(2)Integer 相比 int 的优点
- 支持 null 值:可以表示"未赋值/不存在"的状态,适合数据库、接口参数等场景(int 只能是 0,无法区分 0 和未赋值)
- 支持泛型/集合 :泛型、集合(如
List<Integer>)只能存对象,不能存基本类型,必须用包装类 - 提供丰富工具方法 :如
Integer.parseInt()、Integer.compare()、Integer.toBinaryString()等 - 支持缓存机制:Integer 有缓存池,复用常用对象,提升性能
(3)为什么还要保留 int?
- 性能更高:int 是基本类型,直接存储在栈中,无需对象开销;Integer 是对象,存储在堆中,有对象头、GC 开销
- 内存占用更小:int 占 4 字节,Integer 对象占 16 字节(64 位 JVM 压缩指针),内存开销是 int 的 4 倍
- 避免空指针异常 :int 永远不会为 null,Integer 可能为 null,拆箱时会抛
NullPointerException - 运算效率更高:基本类型运算直接在 CPU 寄存器中完成,包装类需要拆箱,有额外开销
总结:
- 普通运算、局部变量、数组:用
int,性能更好 - 泛型、集合、数据库映射、接口参数:用
Integer,满足面向对象需求
8. 说一下 Integer 的缓存(高频必考题)
核心回答 :
Integer 为了优化性能,实现了缓存池(IntegerCache) ,默认缓存 -128 ~ 127 之间的 Integer 对象。
- 当通过
Integer.valueOf(int)或自动装箱创建该范围内的 Integer 时,会直接复用缓存池中的对象,不会新建对象 - 超出该范围的数值,会每次新建对象
代码示例(面试必写):
java
Integer a = 100; // 自动装箱,调用Integer.valueOf(100),复用缓存
Integer b = 100; // 复用同一个缓存对象
System.out.println(a == b); // true,同一个对象
Integer c = 200; // 超出缓存范围,新建对象
Integer d = 200; // 新建对象
System.out.println(c == d); // false,两个不同对象
补充考点:
-
缓存范围可调整 :通过 JVM 参数
-XX:AutoBoxCacheMax=xxx可以修改缓存上限(下限固定为 -128),例如-XX:AutoBoxCacheMax=1000会缓存-128~1000 -
其他包装类的缓存 :
Byte:缓存-128~127(固定,无法修改)Short:缓存-128~127(固定)Long:缓存-128~127(固定)Character:缓存0~127(ASCII 字符,固定)Boolean:缓存TRUE/FALSE两个对象(固定)Float/Double:没有缓存,因为浮点数没有有限的常用范围
-
== 与 equals 的区别 :
==比较的是对象地址 ,缓存范围内的 Integer 用==为 true,超出为 falseequals()比较的是数值,无论是否在缓存范围内,只要数值相等就为 true
javaSystem.out.println(c.equals(d)); // true,数值都是200
三、补充高频面试题(同类延伸)
9. int 和 Integer 的区别?(高频对比题)
| 对比维度 | int | Integer |
|---|---|---|
| 类型 | 基本数据类型 | 引用数据类型(包装类) |
| 存储位置 | 栈(局部变量)/堆(数组/对象成员) | 堆(对象存储) |
| 默认值 | 0 | null |
| 泛型/集合支持 | 不支持 | 支持 |
| 空指针风险 | 无 | 有(拆箱时null会抛NPE) |
| 内存占用 | 4字节 | 16字节(64位压缩指针) |
| 运算效率 | 高(直接CPU运算) | 低(需要拆箱/装箱) |
| 缓存机制 | 无 | 有(-128~127) |
10. float f = 3.14; 为什么会报错?
核心回答 :
Java 中浮点型字面量默认是 double 类型 ,3.14 是 double,把 double 赋值给 float 属于大范围转小范围 ,必须强制转换,否则编译器报错。
正确写法:
java
float f = 3.14f; // 加f后缀,明确为float类型
// 或
float f = (float)3.14; // 强制转换
11. char 类型能不能存储一个中文汉字?为什么?
核心回答 :
可以。因为 Java 中 char 是 16 位,采用 Unicode 编码,Unicode 字符集包含了所有中文汉字(GB2312/GBK 等编码的汉字都在 Unicode 范围内),一个 char 可以存储一个中文汉字。
java
char c = '中'; // 合法
System.out.println(c); // 输出"中"
12. boolean 类型的取值范围?能不能用 0/1 代替?
核心回答:
boolean只有两个取值:true和false,没有其他值- 不能用 0/1 代替 ,Java 是强类型语言,
boolean和int是完全独立的类型,不能互转(C/C++ 中可以,Java 中不行)
java
// 错误写法
boolean a = 1; // 编译报错
if(1){} // 编译报错
// 正确写法
boolean b = true;
if(b){}
13. 基本数据类型和引用数据类型的区别?
| 对比维度 | 基本数据类型 | 引用数据类型 |
|---|---|---|
| 包含类型 | byte/short/int/long/float/double/char/boolean | 类、接口、数组、包装类 |
| 存储位置 | 栈(局部变量)/堆(数组/对象成员) | 栈存引用,堆存对象实体 |
| 默认值 | 有默认值(如int为0) | 引用默认值为null |
| 传递方式 | 值传递(拷贝数值) | 值传递(拷贝引用地址,本质是引用传递) |
| 空指针 | 无 | 有(引用为null时调用方法会抛NPE) |
| 内存开销 | 小 | 大(有对象头开销) |
14. 为什么 new Integer(127) 和 Integer.valueOf(127) 得到的对象不同?
核心回答:
new Integer(int)会强制新建对象,无论数值是否在缓存范围内,都会在堆中创建新对象Integer.valueOf(int)会优先复用缓存池中的对象,-128~127 范围内直接返回缓存对象,不会新建
java
Integer a = new Integer(127); // 新建对象
Integer b = Integer.valueOf(127); // 复用缓存
System.out.println(a == b); // false,两个不同对象
15. 自动装箱拆箱的常见坑
(1)空指针异常
java
Integer a = null;
int b = a; // 自动拆箱,调用a.intValue(),a为null,抛NullPointerException
(2)缓存范围导致的 == 比较问题
java
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true(缓存)
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false(新建对象)
(3)大量装箱导致的性能问题
循环中频繁自动装箱会创建大量对象,触发 GC,影响性能,建议用基本类型循环
java
// 性能差,频繁装箱
for(Integer i=0; i<1000000; i++){}
// 性能好,用基本类型
for(int i=0; i<1000000; i++){}
四、面试答题技巧
- 回答结构:先给结论,再讲原理,最后补代码示例,面试官最爱听
- 避坑点 :
- 不要混淆
==和equals()对 Integer 的比较 - 不要用
new BigDecimal(double),必须用字符串构造 - 不要在循环中用包装类,避免性能问题
- 不要混淆
- 延伸亮点 :
- 提到 Integer 缓存的 JVM 参数调整
- 提到 BigDecimal 的舍入模式和精度控制
- 提到基本类型和引用类型的内存模型(栈/堆存储)