类型转换
本教程共 100 篇 · 第 13 篇 · 更新于 2026-08-05 · 约 5 分钟阅读
13. 类型转换
本节目标:理解不同类型之间怎么互相转换,分清「自动拓宽」和「强制转换」两种情形,并能避开溢出和精度丢失这两个坑。
为什么要转换
不同类型不能直接随便混。你把一个 int 赋给 double 没问题,反过来把 double 塞进 int 就可能丢东西。
Java 把类型转换分成两类:自动的(安全,编译器帮你做)和强制的(有风险,你自己负责)。
自动拓宽:小范围给大范围
当一个取值范围小的类型,赋值给一个取值范围更大的类型时,Java 自动帮你转,不需要写任何额外代码。
整数类型的「容量」从小到大是:
byte → short → int → long → float → double
```java
(字符 `char` 也可以直接赋给 `int`,因为它本质是个数字。)
```java
int a = 100;
long b = a; // int → long,自动,安全
double c = b; // long → double,自动,安全
char ch = 'A';
int code = ch; // char → int,自动,code 得到 65
为什么安全?因为大碗装小碗的水,一滴不洒。数值信息不会丢。
Note这里有个反直觉的点:
float虽然位数(32 位)比long(64 位)少,但在类型链里long能自动转float。因为float用科学记数法表示,能容纳很大的量级,只是可能损失些精度——Java 仍把它算作「拓宽」。
强制转换:大范围给小范围
反过来,把大范围塞进小范围,编译器不敢自作主张,必须你显式写强制转换,语法是在值前加 (目标类型):
double d = 9.99;
int n = (int) d; // 强制转换,n 得到 9(小数部分直接丢掉)
```java
注意:`(int)` 不是四舍五入,是**直接截断**小数。9.99 变 9,不是 10。
风险更大的是**溢出**。一个 `long` 里的值如果超过 `int` 上限,强转后只剩低位,结果完全错乱:
```java
long big = 3000000000L; // 30 亿,超过 int 上限
int cut = (int) big; // 溢出,cut 变成一个毫无意义的数
Warning强制转换可能静悄悄地出错——编译不报错,运行结果却诡异。凡是看到
(int)、(byte)这类写法,都要在心里打个问号:值真的装得下吗?
反过来,int 也能强转回 char,只要数值落在字符编码范围内:
int code = 66;
char ch = (char) code; // ch 得到 'B'
```bash
这和前面「`char` 自动变 `int`」正好相反:把编码值变回字符,得手动强转,因为 `int` 的范围比 `char` 大,属于窄化。这类字符与编码值的互相转换在处理文本、加密等场景里很常见。
## 运算时的类型提升
做混合运算时,Java 会先把「小个子」提升成「大个子」,再算:
```java
int a = 5;
double b = 2.0;
double result = a + b; // a 被提升为 double,结果是 7.0
还有一个坑专门针对 byte、short、char:它们在参与运算前,统统先被提升成 int。
byte x = 1;
byte y = 2;
// byte z = x + y; // 报错:x+y 结果是 int,不能直接赋给 byte
byte z = (byte) (x + y); // 必须强转回来
```bash
这是新手极易踩的坑。记住:两个 `byte` 相加,结果已经是 `int` 了。
## 数字字面量的默认类型
字面量也有默认类型:整数默认是 `int`,小数默认是 `double`。
```java
long n = 100; // 100 是 int,自动拓宽成 long,OK
// float f = 3.14; // 报错:3.14 是 double,不能自动变 float
float f = 3.14F; // 加 F 显式声明为 float
前者没问题,是因为 int 字面量能自动拓宽进 long。后者要报错,是因为 double 不能自动变窄成 float,得加 F。
字符串与数字互转
开发中经常要把用户在输入框里敲的数字(本质是字符串)转成真正的数字,或者反过来。
数字转字符串,最省事的是拼接空串,或用 String.valueOf:
int age = 18;
String s1 = age + ""; // 拼接,得到 "18"
String s2 = String.valueOf(age); // 更清楚,得到 "18"
```java
**字符串转数字**,用对应包装类的 `parseXxx` 方法:
```java
String s = "123";
int n = Integer.parseInt(s); // 得到整数 123
double d = Double.parseDouble("3.14"); // 得到 3.14
Warning字符串转数字时,内容必须是合法数字,否则运行时会抛异常(比如
Integer.parseInt("abc"))。真实项目里要处理这种意外,初学阶段先记住「转之前内容得是数字」即可。
如果要的不是基本类型 int,而是包装对象 Integer,用 valueOf:
Integer boxed = Integer.valueOf("123"); // 字符串 → Integer 对象
Integer fromInt = Integer.valueOf(123); // int → Integer 对象
Double dObj = Double.valueOf("3.14"); // 同理
```bash
> [!WARNING]
> 老教程里常见 `Integer` 的构造方法(`new Integer` 那种写法)。它从 Java 9 起已标记废弃,Java 25 LTS 中不要再用,一律改用 `Integer.valueOf()`。`valueOf` 不只是替代品,它还会复用 -128~127 的缓存对象,比每次 new 一个新对象更省内存。`Double`、`Boolean` 等其他包装类同理,构造方法一律换成 `valueOf`。
## 运算结果取「最大的那个类型」
混合运算时,结果的类型跟着操作数里「最大」的那个走。记住这条,能预判很多结果类型:
```java
int a = 10;
int b = 3;
double c = a / b; // 这里 a/b 两个都是 int,先按 int 算得 3,再赋给 double 变 3.0
这段容易看走眼:a 和 b 都是 int,所以 a / b 是整数除法,结果 3,赋给 double 后才变成 3.0。想要真正的 3.333,得让其中一个先变 double:
double c = a / (double) b; // 3.3333...
```bash
## 想四舍五入怎么办
`(int)` 是截断,不是四舍五入。要四舍五入用 `Math.round`:
```java
double d = 9.6;
long r = Math.round(d); // 得到 10,最近的整数
int n = (int) Math.round(d); // 想拿 int 再强转一下
Math.round 返回 long,所以常见写法是外面再包一层 (int)。它对负数同样按「四舍五入」规则处理(底层相当于加上 0.5 再截断取整)。
转换规则速查表
把前面散落的规则收拢成一张表,写代码拿不准时回看:
| 场景 | 是否需要强转 | 说明 |
|---|---|---|
int → long / double | 否(自动) | 小范围给大范围,安全 |
char → int | 否(自动) | 字符转编码值 |
long → int | 是 (int) | 可能溢出 |
double → int | 是 (int) | 截断小数 |
byte/short 参与运算 | 结果变 int | 需 (byte)/(short) 转回 |
| 字符串 → 数字 | Integer.parseInt 等 | 内容须合法 |
Tip拿不准要不要强转时,记住铁律:范围变大自动,范围变小手动。 手动就意味着你要为可能的溢出或丢精度负责。
小结
类型转换分自动拓宽和强制转换。小范围赋给大范围自动拓宽,安全;大范围强转给小范围需写 (类型),可能截断小数或溢出。
运算时 byte/short/char 先提升为 int;整数默认 int、小数默认 double。
字符串与数字互转用 String.valueOf 和 Integer.parseInt / Double.parseDouble;要包装对象则用 Integer.valueOf,包装类的构造方法自 Java 9 起已废弃,不要再用。