类型转换规则
核心概念
Java中的类型转换分为两类:
- 隐式转换(自动):小类型 → 大类型,编译器自动完成
- 显式转换(强制):大类型 → 小类型,需手动声明,可能丢失精度
一、隐式类型转换(自动提升)
1.1 转换规则
提升链: byte → short → int → long → float → double
byte b = 10;
short s = b; // ✅ byte → short,自动提升
int i = s; // ✅ short → int,自动提升
long l = i; // ✅ int → long,自动提升
float f = l; // ✅ long → float,自动提升(可能损失精度)
double d = f; // ✅ float → double,自动提升
1.2 特殊规则
① char 的特殊性
char c = 'A'; // ASCII码65
int i = c; // ✅ char → int,自动提升(i = 65)
// 但 char 不能自动转为 byte/short(可能越界)
byte b = c; // ❌ 编译错误
short s = c; // ❌ 编译错误
② 整型与浮点型
// 整型 → 浮点型:自动提升(可能损失精度)
int i = 123456789;
float f = i; // ✅ 但可能精度丢失(float精度仅7位)
// 浮点型 → 整型:必须强制转换
double d = 3.14;
int x = d; // ❌ 编译错误
int x = (int) d; // ✅ 强制转换(截断小数)
1.3 表达式中的类型提升
// 规则:byte/short/char 参与运算时自动提升为 int
byte a = 1;
byte b = 2;
byte c = a + b; // ❌ 编译错误:a + b 结果是 int
// 正确写法
int c = a + b; // ✅
byte c = (byte)(a + b); // ✅ 强制转换
二、显式类型转换(强制转换)
2.1 基本语法
double d = 3.14;
int i = (int) d; // 强制转换,i = 3(截断小数部分)
long l = 100L;
int x = (int) l; // 强制转换,可能溢出
2.2 精度丢失示例
① 浮点 → 整型(截断)
double d = 3.99;
int i = (int) d; // i = 3(直接截断,不是四舍五入)
// 四舍五入需要手动实现
int i = (int) Math.round(d); // i = 4
② 大整型 → 小整型(溢出)
int i = 300;
byte b = (byte) i; // b = 44(发生溢出)
// 原理:300的二进制截取低8位
// 300的二进制:00000000 00000000 00000001 00101100
// 截取低8位:00101100 = 44(十进制)
③ 长整型 → 浮点(精度损失)
long l = 123456789012345L;
float f = l; // float精度不足,可能丢失精度
System.out.println(f); // 1.23456794E14(与原值不完全一致)
三、特殊场景的类型转换
3.1 浮点字面量的类型
// 默认 double 类型
float f = 1.1; // ❌ 编译错误:double不能自动转为float
// 解决方案
float f1 = 1.1f; // ✅ 添加 f 后缀
float f2 = (float) 1.1; // ✅ 强制转换
3.2 复合赋值运算符的隐式转换 ⚠️
核心规则:+=、-=、*=、/= 会自动进行类型转换
short s = 1;
s = s + 1; // ❌ 编译错误:s + 1 是 int 类型
s += 1; // ✅ 编译通过,等价于 s = (short)(s + 1)
// 原理:编译器自动添加强制转换
s += 1; // 等价于 s = (short)(s + 1)
s++; // 等价于 s = (short)(s + 1)
面试陷阱:
byte b = 1;
b = b + 1; // ❌ 错误
b += 1; // ✅ 正确
b++; // ✅ 正确
3.3 三元表达式的类型
// 规则:结果类型是两个分支的公共父类型
int a = 10;
double b = 20.5;
// 结果类型是 double(int自动提升为double)
double result = true ? a : b; // 10.0
// 混合类型
Object obj = true ? 100 : "hello"; // 类型是 Object
四、switch语句的类型限制
4.1 支持的类型
// ✅ 支持
switch (variable) {
case 1: break;
}
// 支持的类型:
// 1. 整型:byte, short, char, int
// 2. 包装类:Byte, Short, Character, Integer
// 3. 枚举:enum
// 4. 字符串:String(Java 7+)
4.2 不支持的类型
// ❌ 不支持
long l = 100L;
switch (l) { } // 编译错误
float f = 1.0f;
switch (f) { } // 编译错误
double d = 1.0;
switch (d) { } // 编译错误
boolean b = true;
switch (b) { } // 编译错误
为什么不支持?
- switch本质是快速跳转表(jump table)或哈希表
- long/float/double范围太大,无法高效实现跳转表
- boolean只有两个值,用if-else更直观
五、类型转换的边界情况
5.1 NaN 和 Infinity
// 浮点数特殊值
double d1 = 0.0 / 0.0; // NaN(Not a Number)
double d2 = 1.0 / 0.0; // Infinity(正无穷)
double d3 = -1.0 / 0.0; // -Infinity(负无穷)
// 转为整型
int i1 = (int) d1; // 0(NaN转为0)
int i2 = (int) d2; // Integer.MAX_VALUE
int i3 = (int) d3; // Integer.MIN_VALUE
5.2 溢出处理
// 整型溢出:循环回绕
int max = Integer.MAX_VALUE; // 2147483647
int overflow = max + 1; // -2147483648(溢出到最小值)
// 如何检测溢出
if (Integer.MAX_VALUE - b < a) {
// a + b 会溢出
}
六、高频面试题
Q1: 隐式类型转换的顺序是什么?
答: byte → short → int → long → float → double char 可以转为 int,但不能转为 byte/short
Q2: 为什么 short s = s + 1 会报错,但 s += 1 不会?
答:
s + 1的结果是 int 类型,不能自动转为 shorts += 1编译器自动添加强制转换:s = (short)(s + 1)
Q3: 浮点数转整数是四舍五入吗?
答: 不是,是直接截断小数部分
int i = (int) 3.99; // i = 3(截断)
int j = (int) Math.round(3.99); // j = 4(四舍五入)
Q4: 为什么 float f = 1.1 会报错?
答:
- 浮点字面量默认是 double 类型
- double 不能自动转为 float(精度更低)
- 解决:
float f = 1.1f或float f = (float) 1.1
Q5: switch 支持哪些类型?为什么不支持 long?
答:
- 支持:byte/short/char/int、包装类、enum、String(7+)
- 不支持:long/float/double/boolean
- 原因:switch基于跳转表,long范围太大,实现效率低
Q6: long 转 float 会丢失精度吗?
答: 会,因为:
- long 精度:64位(19位十进制数)
- float 精度:23位尾数(约7位十进制数)
long l = 123456789012345L;
float f = l; // 精度丢失
Q7: byte a = 1; byte b = 2; byte c = a + b; 为什么报错?
答:
- Java中 byte/short/char 参与运算时自动提升为 int
a + b结果是 int,不能自动转为 byte- 修正:
byte c = (byte)(a + b)或int c = a + b
七、最佳实践
✅ 推荐做法
// 1. 明确类型,避免隐式转换
long count = 100L; // 而非 long count = 100
// 2. 浮点运算使用 BigDecimal
BigDecimal price = new BigDecimal("19.99");
// 3. 强制转换前检查范围
if (longValue <= Integer.MAX_VALUE) {
int intValue = (int) longValue;
}
// 4. 使用包装类方法
int rounded = Math.round(3.5f); // 四舍五入
❌ 避免做法
// 1. 混用类型导致精度丢失
float f = 1.23456789f; // 精度不足
double d = f; // 转为double也无法恢复精度
// 2. 直接截断浮点数
int i = (int) 3.99; // 不是四舍五入
// 3. 忽略溢出风险
int result = Integer.MAX_VALUE + 1; // 溢出为负数
相关链接
- 01-基本类型与包装类 - 8种基本类型的取值范围
- 02-自动装拆箱 - 基本类型与包装类的自动转换
- BigDecimal精度 - 浮点运算的精度问题
记忆口诀
隐式提升按大小,byte short int long float double排 强制转换需谨慎,精度丢失要记牢 复合赋值藏玄机,自动转型编译巧 switch不支持long型,浮点布尔都不要
⬅️ 缓存池机制(Cache Pool) 🏠 00-Java ➡️ String详解
💬 评论