1. 概述

Java使用32位内存来存储Integer类型的数据。因此,Integer(或int)的范围是从-2^31(即-2,147,483,648)到2^31-1(即2,147,483,647)。所以,当我们看到“java:整数数值过大…”这样的错误信息时,通常能快速找到并解决问题。

然而,在某些情况下,遇到这个错误信息时,我们可能不清楚问题出在哪里,需要花费一些时间去调试。本教程将深入探讨导致此类错误的一些陷阱,并解释错误背后的原因。

2. 整数字面量陷阱 #1

当我们将超出上述范围的数字字面量赋值给int变量时,Java编译器会报错。例如,假设我们尝试编译以下代码:

int a = 12345678912345;

编译器会报告如下错误:

java: integer number too large

通过阅读错误信息,我们可以迅速找到问题。可能会认为int类型不适合这么大的数字,于是我们将类型改为long

long a = 12345678912345;

但是,重新编译代码时,依然会收到相同的编译错误:“整数数值过大”。这时,我们可能会疑惑为何虽然声明了变量为long,编译器仍然在抱怨整数?接下来,我们会检查文件是否保存正确、重启IDE等,但问题依然存在。

在Java中,无论数字字面量是否在整数范围内,它都被视为Integer/int类型。若想让一个整数字面量表示为long,必须添加后缀Ll这在Java语言规范中有明确说明:

如果一个整数字面量后面跟着ASCII字母Ll(ell),则其类型为long;否则为int

因此,解决方法是将字面数值后添加L

long a = 12345678912345L;

值得一提的是,如果使用十进制数字字面量且没有后缀,Java会将其视为double。若想表示float,必须添加后缀Ff

float a = 1024.42; // compiler error -  java: incompatible types: possible lossy conversion from double to float
float a = 1024.42F; // compiled

3. 整数字面量陷阱 #2

现在我们知道应为long类型的整数字面量添加L后缀。接下来,看另一个例子:

long a = 007L;

如上所示,这次我们添加了后缀L,并且变量a的类型是long。即使有前导零,代码也能顺利编译。检查a的值,变量a确实持有7,正如预期。于是我们可能会认为Java很智能,忽略了数字的前导零。

现在,我们声明另一个变量:

long b = 008L;

同样,如果我们编译代码,会收到“整数数值过大”的错误。

这次代码中并没有明显的整数标志。而且,long a = 007L;没有问题。为什么long b = 008L却失败了呢?可能需要一段时间来解决这个问题。

实际上,我们又掉入了一个整数字面量的陷阱。因为在Java中,整数字面量也可以以八进制(基数为8)形式表示。进一步来说,八进制整数由一个前导零和0到7的一个或多个数字组成

因此,当我们给变量赋值007L时,一切正常,因为Java解析为八进制整数007,并将其视为long。然而,当我们给变量赋值008L时,008不是一个有效的八进制整数,所以编译器报错。

理解了问题原因后,解决方案就很简单——移除前导零:

long b = 8L;

4. 总结

在这篇文章中,我们讨论了在Java中处理整数字面量时常见的两个陷阱。理解编译错误背后的原因有助于我们快速定位并修复问题。