--- title: "04-类型转换规则" created: 2025-11-25 tags: - Java --- # 类型转换规则 ## 核心概念 Java中的类型转换分为两类: - **隐式转换(自动)**:小类型 → 大类型,编译器自动完成 - **显式转换(强制)**:大类型 → 小类型,需手动声明,可能丢失精度 --- ## 一、隐式类型转换(自动提升) ### 1.1 转换规则 **提升链:** byte → short → int → long → float → double ```java 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 的特殊性 ```java char c = 'A'; // ASCII码65 int i = c; // ✅ char → int,自动提升(i = 65) // 但 char 不能自动转为 byte/short(可能越界) byte b = c; // ❌ 编译错误 short s = c; // ❌ 编译错误 ``` #### ② 整型与浮点型 ```java // 整型 → 浮点型:自动提升(可能损失精度) int i = 123456789; float f = i; // ✅ 但可能精度丢失(float精度仅7位) // 浮点型 → 整型:必须强制转换 double d = 3.14; int x = d; // ❌ 编译错误 int x = (int) d; // ✅ 强制转换(截断小数) ``` ### 1.3 表达式中的类型提升 ```java // 规则: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 基本语法 ```java double d = 3.14; int i = (int) d; // 强制转换,i = 3(截断小数部分) long l = 100L; int x = (int) l; // 强制转换,可能溢出 ``` ### 2.2 精度丢失示例 #### ① 浮点 → 整型(截断) ```java double d = 3.99; int i = (int) d; // i = 3(直接截断,不是四舍五入) // 四舍五入需要手动实现 int i = (int) Math.round(d); // i = 4 ``` #### ② 大整型 → 小整型(溢出) ```java int i = 300; byte b = (byte) i; // b = 44(发生溢出) // 原理:300的二进制截取低8位 // 300的二进制:00000000 00000000 00000001 00101100 // 截取低8位:00101100 = 44(十进制) ``` #### ③ 长整型 → 浮点(精度损失) ```java long l = 123456789012345L; float f = l; // float精度不足,可能丢失精度 System.out.println(f); // 1.23456794E14(与原值不完全一致) ``` --- ## 三、特殊场景的类型转换 ### 3.1 浮点字面量的类型 ```java // 默认 double 类型 float f = 1.1; // ❌ 编译错误:double不能自动转为float // 解决方案 float f1 = 1.1f; // ✅ 添加 f 后缀 float f2 = (float) 1.1; // ✅ 强制转换 ``` ### 3.2 复合赋值运算符的隐式转换 ⚠️ **核心规则:`+=`、`-=`、`*=`、`/=` 会自动进行类型转换** ```java 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) ``` **面试陷阱:** ```java byte b = 1; b = b + 1; // ❌ 错误 b += 1; // ✅ 正确 b++; // ✅ 正确 ``` ### 3.3 三元表达式的类型 ```java // 规则:结果类型是两个分支的公共父类型 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 支持的类型 ```java // ✅ 支持 switch (variable) { case 1: break; } // 支持的类型: // 1. 整型:byte, short, char, int // 2. 包装类:Byte, Short, Character, Integer // 3. 枚举:enum // 4. 字符串:String(Java 7+) ``` ### 4.2 不支持的类型 ```java // ❌ 不支持 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 ```java // 浮点数特殊值 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 溢出处理 ```java // 整型溢出:循环回绕 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: 浮点数转整数是四舍五入吗? **答:** 不是,是**直接截断小数部分** ```java 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位十进制数) ```java 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` --- ## 七、最佳实践 ### ✅ 推荐做法 ```java // 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); // 四舍五入 ``` ### ❌ 避免做法 ```java // 1. 混用类型导致精度丢失 float f = 1.23456789f; // 精度不足 double d = f; // 转为double也无法恢复精度 // 2. 直接截断浮点数 int i = (int) 3.99; // 不是四舍五入 // 3. 忽略溢出风险 int result = Integer.MAX_VALUE + 1; // 溢出为负数 ``` --- ## 相关链接 - [[01-基本类型与包装类|01-基本类型与包装类]] - 8种基本类型的取值范围 - [[02-自动装拆箱|02-自动装拆箱]] - 基本类型与包装类的自动转换 - [[03-BigDecimal|BigDecimal精度]] - 浮点运算的精度问题 --- ## 记忆口诀 > 隐式提升按大小,byte short int long float double排 > 强制转换需谨慎,精度丢失要记牢 > 复合赋值藏玄机,自动转型编译巧 > switch不支持long型,浮点布尔都不要 --- ⬅️ [[03-缓存池机制(Cache Pool)|缓存池机制(Cache Pool)]] 🏠 [[00-Java|00-Java]] ➡️ [[01-String详解|String详解]]