跳到内容

第 1 卷 / 第 2 章 / 第 3 课

2.3 数值转换、提升与精度 ​

前置知识:基本类型的范围与边界

预计时间:30 分钟

完成标志:能区分拓宽、窄化、二元数值提升和字符串解析

拓宽基本类型转换 ​

拓宽转换不需要显式 cast:

java
byte small = 100;
int count = small;
long total = count;
double measurement = total;

常用合法方向:

text
byte  → short → int → long → float → double
                ↑
char ───────────┘

这张图是速记,不是“每一步都无损”的承诺:

  • byte、short、char 到 int 能精确表示;
  • int 到 long 能精确表示;
  • float 到 double 能精确保存原有 float 值;
  • int 到 float、long 到 float,以及 long 到 double 可能丢失低位精度;
  • 拓宽转换不会因目标范围太小而溢出,但浮点结果可能只保留近似值。

实际验证:

java
long original = 9_007_199_254_740_993L;
double widened = original;
long roundTrip = (long) widened;

System.out.println(original);
System.out.println(widened);
System.out.println(roundTrip);
System.out.println(original == roundTrip);

在这个例子中,roundTrip 会变成 9_007_199_254_740_992,最终比较为 false。

窄化基本类型转换 ​

目标类型不能表示所有源值时,通常需要显式 cast:

java
double weight = 85.7;
int kilograms = (int) weight;
System.out.println(kilograms); // 85

浮点转整数向零截断,不是四舍五入:

java
System.out.println((int) 3.9);  // 3
System.out.println((int) -3.9); // -3

转为 int 或 long 时,还有三个边界:

  • NaN 转整数得到 0;
  • 超过目标整数最大值时,结果钳制到最大值;
  • 小于目标整数最小值时,结果钳制到最小值。

转成 byte、short 或 char 要多走一步:先按上述规则转为 int,再执行整数窄化。因此 (byte) 1000.0 是 -24,并不会钳制为 Byte.MAX_VALUE;(byte) Double.POSITIVE_INFINITY 则是 -1。

整数窄化只保留目标宽度的低位:

java
int value = 1000;
byte narrowed = (byte) value;
System.out.println(narrowed); // -24

cast 表示“允许按规范执行转换”,不表示结果符合业务意图。转换前应做范围检查:

java
if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
    throw new IllegalArgumentException("out of byte range: " + value);
}
byte safe = (byte) value;

常量表达式的特殊窄化 ​

下面能编译:

java
byte level = 100;
char marker = 65;

因为右侧是编译期常量表达式,值落在目标类型范围内。换成普通 int 变量则需要 cast:

java
int source = 100;
// byte level = source; // 编译错误
byte level = (byte) source;

常量超范围仍会被拒绝:

java
// byte invalid = 200;

一元与二元数值提升 ​

byte、short、char 参与大多数算术运算时会先提升为 int:

java
byte left = 10;
byte right = 20;
// byte sum = left + right; // 编译错误:结果是 int
int sum = left + right;

复合赋值包含隐式转换:

java
byte count = 10;
count += 1;          // 近似等价于 count = (byte) (count + 1)
// count = count + 1; // 右侧是 int,不能直接赋给 byte

“近似等价”很重要:左侧表达式只求值一次,后面学习数组和副作用时会看到差别。

二元数值提升大体遵循:

  1. 有 double,另一方转为 double;
  2. 否则有 float,另一方转为 float;
  3. 否则有 long,另一方转为 long;
  4. 否则双方提升为 int。
java
int units = 5;
double factor = 2.0;
double result = units * factor;

不要用“结果永远是最大的类型”代替正式规则;字符串连接、移位、条件表达式和常量表达式还有各自规则。

数值转换不等于解析文本 ​

cast 只处理兼容的基本类型或引用类型关系,不能把字符串直接 cast 成整数:

java
String text = "42";
int value = Integer.parseInt(text);

无效文本会在运行时抛出 NumberFormatException:

java
try {
    int parsed = Integer.parseInt("forty-two");
    System.out.println(parsed);
} catch (NumberFormatException error) {
    System.out.println("not an integer");
}

数字转字符串可以使用 Integer.toString(value) 或 String.valueOf(value)。

装箱与拆箱边界 ​

java
Integer boxed = 100; // 自动装箱
int plain = boxed;   // 自动拆箱

包装类型可以是 null,拆箱 null 会抛 NullPointerException:

java
Integer missing = null;
// int value = missing; // 运行时失败

这不是基本类型数值拓宽或窄化,排错时要分开判断。集合章节会进一步处理包装类型。

Python 与 C++ 对照 ​

Python 的 int(3.9) 是运行时调用,用于构造整数值,结果向零截断;int("42") 还承担文本解析。普通 Python int 支持任意精度,但转成固定宽度的库类型时仍可能出现范围问题。

C++ 提供 static_cast 等多种显式转换形式,也保留 C 风格 cast。Java 整数运算的回绕由语言规范定义;C++ 有符号整数溢出通常是未定义行为,无符号整数按模运算。不能把 Java 实验结果直接移植为 C++ 推理。

专业速查 ​

任务做法
小整数到 int/long拓宽转换,通常无须 cast
long 到 double无须 cast,但检查精度要求
浮点到整数显式 cast;向零截断并处理 NaN/越界
int 到 byte先检查范围,再 cast
byte + byte结果通常是 int
文本到整数Integer.parseInt,处理格式错误
Integer 到 int注意 null 拆箱风险

离开这一页前 ​

拿到一次转换,依次问范围、精度和失败方式。能解释 byte + byte 为什么得到 int,知道浮点转整数向零截断,并把 cast、文本解析与拆箱分开判断,就可以进入实验。

→ 下一步:变量、类型与转换实验

规范入口:JLS §5.1:转换的种类、§5.6:数值上下文与提升。

Built with VitePress | Software Systems Atlas