目录
- [1. 引言与案例](#1. 引言与案例 "#sec-1")
- [2. 什么是宽化转换](#2. 什么是宽化转换 "#sec-2")
- [3. 字面量的默认类型](#3. 字面量的默认类型 "#sec-3")
- [4. 算术运算中的提升](#4. 算术运算中的提升 "#sec-4")
- [5. 与装箱拆箱及重载决议](#5. 与装箱拆箱及重载决议 "#sec-5")
- [6. 常见坑与实用建议](#6. 常见坑与实用建议 "#sec-6")
- [7. 小测验](#7. 小测验 "#sec-7")
- [8. 一句话记忆](#8. 一句话记忆 "#sec-8")
1. 引言与案例

你在代码中写下:
java
long a = 999999999; // 正常编译运行
为什么可行?因为:
- 整数字面量默认类型是 int;
999999999位于int的取值范围内(-2147483648 ~ 2147483647);- 将
int赋给long属于宽化转换 (从较小范围到较大范围),是安全且无需强转的。
对比:如果字面量超出 int 范围,就必须显式标记为 long:
java
long x = 2147483647; // OK(仍在 int 范围内)
long y = 2147483648; // 编译错误(默认按 int 解析,已溢出)
long z = 2147483648L; // OK(L 表示 long 字面量)
long w = 3000000000; // 编译错误
long v = 3000000000L; // OK
2. 什么是宽化转换
- 定义:把类型"向更宽的表示能力"转换的过程,通常是隐式且安全的,不需要强制类型转换。
- 原始类型宽化链路 :
byte → short → int → long → float → doublechar → int → long → float → double
- 引用类型宽化 :子类到父类,或实现类到接口,例如
ArrayList → List → Collection → Object。
注意:虽然"整数 → 浮点"也被视为宽化(比如 int → float、long → double),但它可能带来精度表示差异(浮点无法精确表示所有大整数)。
3. 字面量的默认类型
- 整数字面量默认
int:1、0xFF、0b1010、07等。 - 浮点字面量默认
double:1.0、3.14、1e3等。 - 需要
long:在整数字面量后加L(推荐大写L,避免和数字1混淆)。 - 需要
float:在浮点字面量后加F。 - 字符字面量如
'a'的类型是char。
4. 算术运算中的提升
在算术运算中,较小整数类型(byte、short、char)会先被提升为 int,再参与计算,结果至少是 int:
java
byte p = 1, q = 2;
var r = p + q; // r 的类型是 int
// byte s = p + q; // 编译错误:需要强制转换
byte s = (byte) (p + q); // 显式强转后 OK
byte a1 = 1 + 2; // OK:编译期常量折叠,且结果在 byte 范围内
byte a2 = 1 + p; // 编译错误:含变量参与,结果提升为 int
混合运算遵循"向更宽类型靠拢"的规则:int → long → float → double。例如:
java
double d = 1L + 1.0F; // 结果是 double
5. 与装箱拆箱及重载决议
它们是两套不同机制:
- 宽化转换:原始类型之间或引用类型继承层次间的"变宽"。不创建对象,编译期通常可判定。
- 自动装箱/拆箱 :原始类型与包装类型之间的自动转换(
int ↔ Integer、long ↔ Long等)。可能创建对象,拆箱时还可能触发NullPointerException。
在方法重载选择上,优先级通常为:原始类型宽化 > 自动装箱/拆箱 > 可变参数 。并且,编译器不会 做"装箱再宽化"的组合步(例如不存在 int → Integer → Long 的路径)。
示例:
java
void f(long x) {}
void f(Integer x) {}
void f(int... xs) {}
f(1); // 调用 f(long) ------ 原始类型宽化优先
void g(Long x) {}
// g(1); // 编译错误:不能从 int 直接变为 Long(不会先装箱为 Integer 再"宽化"为 Long)
6. 常见坑与实用建议
-
超出
int范围的整数字面量必须加L,否则以int解析会编译失败。 -
整数除法会截断小数 :
1/2 == 0。若要小数结果,用1.0、1d或1F触发浮点运算:javadouble r1 = 1 / 2; // 0.0 double r2 = 1.0 / 2; // 0.5 -
整数 → 浮点虽属"宽化",但可能有精度差异 (尤其是
long → float、long → double)。对精度敏感的数量(如金额)请使用BigDecimal。 -
拆箱可能 NPE :
Integer n = null; int x = n; // NPE。
7. 小测验
-
下列哪行会编译失败?为什么?
javalong a = 2147483647; long b = 2147483648; long c = 2147483648L; -
下面的调用会选哪个重载?
javavoid h(long x) {} void h(Integer x) {} h(1); -
下面的结果分别是什么?
javadouble x = 1 / 2; double y = 1.0 / 2; -
说明下面为何编译错误,如何修正?
javabyte b = 1; byte c = 2; byte d = b + c;
答案:
long b = 2147483648;失败。2147483648默认按int解析且超范围;写成2147483648L即可。- 选择
h(long),因原始类型宽化优先于装箱。 x == 0.0(整数除法先算出 0 再宽化),y == 0.5。b + c提升为int,赋给byte需强转:byte d = (byte) (b + c);。
8. 一句话记忆
- 整数字面量默认
int,浮点字面量默认double;超出范围要用后缀(L/F)。 - "小到大"的隐式转换叫宽化转换;运算时小整型先提升为
int。 - 重载决议:原始宽化 > 装箱/拆箱 > 可变参数;不做"装箱再宽化"。