两大类:基本类型和引用类型
Java 中的每个值都有类型,而类型分为两大类。基本类型直接存放原始值:一个数字、一个字符,或者真/假。引用类型存放指向内存中其他位置的对象的引用(指针):String、数组,以及你或各种库定义的每一个类。
这种区别很重要,因为基本类型很轻量,按值复制,而引用变量指向的是共享的对象。先从八种基本类型开始,它们是其他一切的基础。
八种基本类型
Java 正好有八种内置基本类型。六种是数字,一种是字符,一种是布尔值:
有几点需要先注意:2_000_000 中的下划线只是视觉上的分隔符,编译器会忽略它们;L 和 f 后缀是必需的(下文会详细说明);char 用单引号,而 String 用双引号。
选择数值类型
你几乎用不到 byte 或 short,它们只在超大数组中才能节省内存。真正要做的选择是:整数用 int 还是 long,小数用 float 还是 double。
| 类型 | 大小 | 适用场景 |
|---|---|---|
int | 32 位 | 整数的默认选择(最大约 21 亿) |
long | 64 位 | 超过约 20 亿的值:毫秒时间戳、文件大小 |
double | 64 位 | 小数的默认选择,精度好 |
float | 32 位 | 只在内存紧张且可以牺牲精度时使用 |
陷阱在于溢出。int 超出范围时会悄悄回绕:不报错,只是得出错误的结果:
maxInt + 1 溢出成 -2147483648,因为两个操作数都是 int,结果仍然是 int。先把其中一边转换成 long,计算就会以 64 位进行。当值可能很大时,尤其是从 1970 年起算的毫秒数,就用 long。
字面量及其后缀
直接写出的数字字面量有默认类型,这一点常常坑人。100 是 int;3.14 是 double。要写 long 或 float 字面量,必须加上后缀:
去掉 8_000_000_000L 中的 L,代码就无法编译,因为这个字面量在赋给 long 变量之前就已经超出了 int 的范围。去掉 0.5f 中的 f,Java 会报错说 double 不能在不转换的情况下赋给 float。后缀不区分大小写,但要用大写的 L:小写的 l 看起来和数字 1 一模一样。
char 其实是伪装的数字
char 存放一个 16 位的 Unicode 码元,这意味着它也可以当作一个小的无符号整数来用。你可以对它做算术运算,并通过类型转换来回转换:
这在移动字母时偶尔很方便,但不要用它来处理文本,那是 String 的工作。注意 char 用单引号('A');双引号("A")会生成一个只有一个字符的 String,那是完全不同的类型。
默认值(以及局部变量的陷阱)
没有初始化的字段和数组元素会得到一个合理的默认值。但这只适用于字段和数组元素:局部变量没有默认值,在赋值之前使用它会导致编译错误,而不是运行时才出现的意外。
如果你在 main 里写 int x; System.out.println(x);,编译器会拒绝它,并提示“variable x might not have been initialized”。这种严格是一种特性:它防止你读到垃圾值。需要记住的默认值:数值类型为 0,boolean 为 false,char 为 '\u0000',所有引用类型为 null。
下一步:字符串
String 是你最常用的引用类型:文本、拼接起来的值、用户输入。它的行为和这里的基本类型不同:它是对象,它不可变,比较字符串也有自己的陷阱。下一页会讲到它。
常见问题
Java 有多少种数据类型?
Java 有八种基本类型:byte、short、int、long、float、double、char 和 boolean。其他一切,包括 String、数组和任何对象,都是引用类型。所以内置基本类型的数量固定为八种,而引用类型没有上限,因为你(以及各种库)可以定义新的类。
Java 中 int 和 long 有什么区别?
两者都存放整数,但 int 是 32 位的(范围约为 ±21 亿),long 是 64 位的(范围约为 ±922 亿亿)。默认用 int;当值可能超过 20 亿时换成 long,比如以毫秒计的时间戳、文件大小、很大的计数器。long 字面量需要加 L 后缀,例如 long big = 10000000000L;。
Java 中 int 的默认值是多少?
int 字段的默认值是 0,但只有字段和数组元素才有默认值。long 默认为 0L,double 默认为 0.0,boolean 默认为 false,char 默认为 '\u0000',任何引用类型默认为 null。局部变量完全没有默认值:必须先赋值再使用,否则代码无法编译。