类型转换规则

核心概念

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 类型,不能自动转为 short
  • s += 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.1ffloat 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;  // 溢出为负数

相关链接


记忆口诀

隐式提升按大小,byte short int long float double排 强制转换需谨慎,精度丢失要记牢 复合赋值藏玄机,自动转型编译巧 switch不支持long型,浮点布尔都不要


⬅️ 缓存池机制(Cache Pool) 🏠 00-Java ➡️ String详解