第 5 章:八种基本数据类型
学习目标
- 掌握 Java 8 种基本数据类型的字节数、取值范围
- 理解整数默认 int、浮点默认 double 的设计原因
- 了解字符编码(ASCII / Unicode / UTF-8)底层原理
- 避免基本类型使用的常见坑
一、Java 的 8 种基本类型
Java 只有 8 种基本数据类型(Primitive Types),分三大类:
二、8 种类型速查表
| 类型 | 字节 | 位数 | 取值范围 | 默认值 | 包装类 |
|---|---|---|---|---|---|
byte | 1 | 8 | -128 ~ 127 | 0 | Byte |
short | 2 | 16 | -32,768 ~ 32,767 | 0 | Short |
int | 4 | 32 | -2³¹ ~ 2³¹-1(约 ±21 亿) | 0 | Integer |
long | 8 | 64 | -2⁶³ ~ 2⁶³-1 | 0L | Long |
float | 4 | 32 | 约 ±3.4×10³⁸(7 位有效数字) | 0.0f | Float |
double | 8 | 64 | 约 ±1.8×10³⁰⁸(15 位有效数字) | 0.0d | Double |
char | 2 | 16 | 0 ~ 65,535(Unicode 字符) | '\u0000' | Character |
boolean | - | - | true / false | false | Boolean |
字节数可视化
记忆口诀:byte < short < int < long(按字节数从小到大),float = int(4 字节),double = long(8 字节),char = short(2 字节)。
三、整数类型
3.1 四种整数的字节数与范围
// byte: -128 ~ 127
byte b1 = 100;
byte b2 = -128;
byte b3 = 127;
// byte b4 = 128; // ❌ 编译报错:超出 byte 范围
// short: -32,768 ~ 32,767
short s1 = 30000;
short s2 = -32768;
// short s3 = 50000; // ❌ 编译报错
// int: 约 ±21 亿(最常用)
int i1 = 2000000000;
int i2 = -2_000_000_000; // Java 7+ 支持下划线分隔,便于阅读
// int i3 = 3000000000; // ❌ 编译报错:超过 int 范围
// long: 极大范围(需要加 L 后缀)
long l1 = 100L;
long l2 = 9000000000L; // ✅ 90 亿,需要 L 后缀
// long l3 = 9000000000; // ❌ 编译报错:默认是 int,超出 int 范围3.2 为什么默认是 int?
Java 设计者认为:int 是 32 位 CPU 原生支持的速度最快的整数类型。
// 整数默认是 int
int x = 100; // 100 被识别为 int
// long y = 100; // OK,自动从 int 转为 long(小到大自动转)
// long z = 9000000000; // ❌ 9000000000 默认是 int,超出范围
long z = 9000000000L; // ✅ 加 L 表示是 long整数后缀
long加L或l(推荐L,因为l容易和数字1混淆)- 没有
int后缀,默认就是int
3.3 整数的三种进制
int decimal = 100; // ① 十进制(默认)
int hex = 0xFF; // ② 十六进制:255(0x 前缀)
int octal = 077; // ③ 八进制:63(0 前缀,注意不是 O)
int binary = 0b1010; // ④ 二进制:10(0b 前缀,JDK 7+)输出验证:
System.out.println(decimal); // 100
System.out.println(hex); // 255
System.out.println(octal); // 63
System.out.println(binary); // 103.4 下划线分隔符(Java 7+)
int creditCardNumber = 1234_5678_9012_3456; // ① 信用卡号
long socialSecurityNumber = 999_99_9999L; // ② 社保号
int hexWithUnderscore = 0xFF_EC_DE_5E; // ③ 字节对齐规则:只能在数字之间,不能在开头、末尾、小数点前后。
// ❌ 错误用法
// int x = _100; // 不能在开头
// int y = 100_; // 不能在末尾
// double z = 3_.14; // 不能在小数点后
// double w = 3._14; // 不能在小数点前四、浮点类型
4.1 float vs double
// double:双精度,约 15 位有效数字(默认,推荐)
double d1 = 3.141592653589793;
double d2 = 1.7976931348623157E308; // 科学计数法
// float:单精度,约 7 位有效数字(需要加 f 后缀)
float f1 = 3.14f;
float f2 = 1.4E-45f;4.2 精度对比实验
double d = 0.1 + 0.2;
System.out.println(d); // 0.30000000000000004(不是 0.3!)
float f = 0.1f + 0.2f;
System.out.println(f); // 0.3(float 也有误差,但显示不同)浮点数不能精确表示小数!
这是 IEEE 754 浮点数的本质问题。金融计算绝对不能用 float/double,必须用
BigDecimal。javaBigDecimal a = new BigDecimal("0.1"); BigDecimal b = new BigDecimal("0.2"); System.out.println(a.add(b)); // 0.3 精确
4.3 为什么默认是 double?
double x = 3.14; // 3.14 被识别为 double
// float y = 3.14; // ❌ 编译报错:double 转 float 精度损失
float y = 3.14f; // ✅ 加 f 才是 float原因:现代 CPU 普遍支持 64 位双精度运算,double 是性能与精度的平衡点。
4.4 三个特殊的浮点值
double positiveInfinity = 1.0 / 0.0; // +Infinity
double negativeInfinity = -1.0 / 0.0; // -Infinity
double notANumber = 0.0 / 0.0; // NaN
System.out.println(positiveInfinity); // Infinity
System.out.println(negativeInfinity); // -Infinity
System.out.println(notANumber); // NaN
System.out.println(Double.isNaN(notANumber)); // true五、字符类型 char
5.1 char 本质上是整数
char 是 16 位无符号整数,取值范围 0 ~ 65535,对应 Unicode 字符集。
char c1 = 'A'; // 字符字面量
char c2 = 65; // ASCII 码:65 = 'A'
char c3 = '\u0041'; // Unicode:'\u0041' = 'A'
char c4 = '中'; // 直接赋值中文
char c5 = '\u4e2d'; // Unicode:'中' 的编码是 4E2D
System.out.println(c1); // A
System.out.println(c2); // A
System.out.println(c3); // A
System.out.println(c4); // 中
System.out.println(c5); // 中5.2 字符编码的演进
为什么 Java 的 char 是 2 字节?
因为 Java 诞生时(1995)Unicode 还只有 65,536 个字符(Basic Multilingual Plane,BMP),用 16 位(2 字节)刚好够用。
但后来 Unicode 扩展到了超过 14 万字符,需要用 4 字节(代理对)。Java 5 引入了 int 码点 来表示完整的 Unicode:
// 简单字符(一个 char)
char c = 'A'; // 单个 16 位
// 辅助平面字符(两个 char = 一个码点)
String emoji = "😀"; // 这个 emoji 实际是 2 个 char
int codePoint = emoji.codePointAt(0); // 128512
System.out.println(codePoint); // 128512
// 字符数(码点数)而不是 char 数
System.out.println(emoji.length()); // 2(char 长度)
System.out.println(emoji.codePointCount(0, emoji.length())); // 1(码点长度)5.3 转义字符
char newline = '\n'; // 换行 LF(Unix)
char carriageReturn = '\r'; // 回车 CR(老 Mac)
char tab = '\t'; // 制表符
char backslash = '\\'; // 反斜杠本身
char singleQuote = '\''; // 单引号本身
char doubleQuote = '\"'; // 双引号本身
char nullChar = '\0'; // 空字符
char unicode = '\u0041'; // Unicode 字符(4 位 16 进制)六、布尔类型 boolean
Java 的 boolean 只有两个值:true / false。
boolean isActive = true;
boolean hasPermission = false;
boolean isAdult = age >= 18; // 表达式结果6.1 重要:boolean 不参与数值运算
// ❌ C/C++ 中常见用法(Java 不允许)
// int x = true; // 编译报错:boolean 不能转 int
// boolean flag = 1; // 编译报错:int 不能转 boolean
// ✅ 正确写法
boolean flag = (x > 0); // 表达式结果6.2 boolean 在内存中的大小
JVM 规范没有明确规定 boolean 占多少字节,实际实现:
boolean flag = true; // 占 4 字节
boolean[] flags = new boolean[1000]; // 占 1000 字节(每个 1 字节)七、类型转换
7.1 自动类型转换(小 → 大)
byte b = 10;
short s = b; // 自动转换:byte → short
int i = s; // 自动转换:short → int
long l = i; // 自动转换:int → long
float f = l; // 自动转换:long → float(注意:可能损失精度)
double d = f; // 自动转换:float → double7.2 强制类型转换(大 → 小)
double d = 3.14;
int i = (int) d; // 强制转换:3.14 → 3(小数部分丢失)
long l = 100L;
int i2 = (int) l; // 强制转换:100L → 100
int bigNum = 200;
byte b = (byte) bigNum; // 强制转换:200 → -56(高位截断!)7.3 转换时的精度损失
// 浮点转整数:直接截断小数部分(不四舍五入)
double d1 = 3.9;
int i1 = (int) d1; // 3(不是 4)
// 大整数转小整数:高位截断
int i2 = 0x12345678;
byte b2 = (byte) i2; // 0x78(只剩最低 8 位)
// 整数转浮点:可能损失精度
long l3 = 123456789012345L;
double d3 = l3; // 1.23456789012345E14(精度损失)警惕类型转换的坑
- 整数除法会丢失小数部分:
5 / 2 = 2(不是 2.5)- 浮点默认 double,赋值给 float 要加
f- 长整数超过 int 范围必须加
L- 强制转换可能造成数据丢失
八、为什么 Java 不支持无符号类型?
Java 的整数类型全部是有符号(signed),这是设计选择。
| 语言 | 无符号支持 |
|---|---|
| C / C++ | 支持 unsigned int 等 |
| Java | 不支持(除 char) |
| Go | 支持 uint 等 |
Java 不支持的原因:
- 跨平台考虑:无符号类型在不同 CPU 上的行为不一致
- 简化设计:减少类型数量,降低学习成本
- 业务场景少:99% 的业务代码不需要无符号
替代方案
如果需要表示大数字(如文件大小、位运算标志),使用
long:javalong maxFileSize = Long.MAX_VALUE; // 9.2 × 10^18
九、完整代码示例
/**
* 基本数据类型综合示例
*
* 配套文档:第 5 章 八种基本数据类型
* 运行方式:javac PrimitiveTypesDemo.java && java PrimitiveTypesDemo
*/
public class PrimitiveTypesDemo {
public static void main(String[] args) {
// ============ 整数类型 ============
byte byteVar = 127; // ① 字节(-128 ~ 127)
short shortVar = 32767; // ② 短整型(-32768 ~ 32767)
int intVar = 2147483647; // ③ 整型(约 ±21 亿,最常用)
long longVar = 9223372036854775807L; // ④ 长整型(注意 L 后缀)
System.out.println("=== 整数类型 ===");
System.out.println("byte: " + Byte.MIN_VALUE + " ~ " + Byte.MAX_VALUE);
System.out.println("short: " + Short.MIN_VALUE + " ~ " + Short.MAX_VALUE);
System.out.println("int: " + Integer.MIN_VALUE + " ~ " + Integer.MAX_VALUE);
System.out.println("long: " + Long.MIN_VALUE + " ~ " + Long.MAX_VALUE);
// ============ 浮点类型 ============
float floatVar = 3.14f; // ⑤ 单精度(注意 f 后缀)
double doubleVar = 3.141592653589793; // ⑥ 双精度(默认,推荐)
System.out.println("\n=== 浮点类型 ===");
System.out.println("float: " + Float.MIN_VALUE + " ~ " + Float.MAX_VALUE);
System.out.println("double: " + Double.MIN_VALUE + " ~ " + Double.MAX_VALUE);
System.out.println("0.1 + 0.2 = " + (0.1 + 0.2)); // 经典精度问题
// ============ 字符类型 ============
char charVar1 = 'A'; // ⑦ 字符字面量
char charVar2 = 65; // ⑧ ASCII 码
char charVar3 = '\u4e2d'; // ⑨ Unicode
char charVar4 = '中'; // ⑩ 直接赋值中文
System.out.println("\n=== 字符类型 ===");
System.out.println("'A' = " + charVar1);
System.out.println("65 = " + charVar2);
System.out.println("'\\u4e2d' = " + charVar3);
System.out.println("'中' = " + charVar4);
// ============ 布尔类型 ============
boolean isJavaFun = true; // ⑪ 布尔
boolean isFishTasty = false;
System.out.println("\n=== 布尔类型 ===");
System.out.println("Java 有趣吗? " + isJavaFun);
System.out.println("鱼好吃吗? " + isFishTasty);
// ============ 类型转换 ============
System.out.println("\n=== 类型转换 ===");
// 自动转换(小 → 大)
int intVal = 100;
long longVal = intVal; // ⑫ 自动 int → long
double doubleVal = longVal; // ⑬ 自动 long → double
System.out.println("int → long: " + longVal);
System.out.println("long → double: " + doubleVal);
// 强制转换(大 → 小)
double pi = 3.14159;
int truncatedPi = (int) pi; // ⑭ 强制 double → int(截断小数)
System.out.println("double → int(截断): " + truncatedPi);
// 整数除法截断
int result = 5 / 2; // ⑮ 整数除法:2.5 → 2
System.out.println("5 / 2 = " + result);
double correctResult = 5.0 / 2; // ⑯ 浮点除法:2.5
System.out.println("5.0 / 2 = " + correctResult);
}
}运行结果:
=== 整数类型 ===
byte: -128 ~ 127
short: -32768 ~ 32767
int: -2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807
=== 浮点类型 ===
float: 1.4E-45 ~ 3.4028235E38
double: 4.9E-324 ~ 1.7976931348623157E308
0.1 + 0.2 = 0.30000000000000004
=== 字符类型 ===
'A' = A
65 = A
'\u4e2d' = 中
'中' = 中
=== 布尔类型 ===
Java 有趣吗? true
鱼好吃吗? false
=== 类型转换 ===
int → long: 100
long → double: 100.0
double → int(截断): 3
5 / 2 = 2
5.0 / 2 = 2.5十、常见面试题
Q1: 为什么 int 是最常用的整数类型?
- CPU 原生支持:32 位 CPU 运算 int 最快
- 范围足够:约 ±21 亿能覆盖 99% 业务
- JVM 优化:int 在 JVM 字节码里有专门的
iadd、imul指令
Q2: float 和 double 怎么选?
| 场景 | 推荐 |
|---|---|
| 科学计算 / 图形 / 游戏 | double(精度更高) |
| 大数据量(如机器学习的特征) | float(节省一半内存) |
| 金融 / 货币计算 | BigDecimal(精确小数) |
Q3: char 能不能存中文?
可以,但有坑。
- ✅ BMP 平面(基本中日韩)的字符:
char c = '中';没问题 - ❌ 辅助平面(部分罕见汉字、emoji):需要用
String存代理对
// ✅ BMP 字符
char c = '中'; // 单个 char
// ❌ 辅助平面字符(如 emoji)
// char e = '😀'; // 编译报错:字面量超出 char 范围
String emoji = "😀"; // 用 String,存的是代理对Q4: 不用 BigDecimal 怎么四舍五入?
double d = 3.7;
long rounded = Math.round(d); // 4(四舍五入返回 long)但金融计算必须用 BigDecimal,浮点四舍五入仍有精度问题。
十一、本章小结
| 要点 | 关键 |
|---|---|
| 8 种基本类型 | byte / short / int / long / float / double / char / boolean |
| 整数默认 int | 大字面量必须加 L |
| 浮点默认 double | float 必须加 f 后缀 |
| char 是 16 位 | Unicode 编码,但 emoji 需要 String |
| boolean 不参与数值 | 不能与 int 互转 |
| 自动转换 | 小 → 大,无需操作 |
| 强制转换 | 大 → 小,可能精度损失 |
动手练习
练习 1:基础题
声明并打印所有 8 种基本类型的极值:
System.out.println("byte 范围: " + Byte.MIN_VALUE + " ~ " + Byte.MAX_VALUE);
// ... 其他 7 种类型练习 2:精度问题实验
System.out.println("0.1f + 0.2f = " + (0.1f + 0.2f)); // 浮点
System.out.println("(int)3.99 = " + (int)3.99); // 截断
System.out.println("(int)-3.99 = " + (int)-3.99); // 截断(不是四舍五入!)
System.out.println("5 / 2 = " + (5 / 2)); // 整数除法
System.out.println("5.0 / 2 = " + (5.0 / 2)); // 浮点除法练习 3:挑战题
写一个 SafeCast.java,安全地将 double 转 int,四舍五入而不是截断。
提示:用 Math.round() 返回 long,再强转 int。
double d1 = 3.4;
double d2 = 3.6;
System.out.println(safeCast(d1)); // 3
System.out.println(safeCast(d2)); // 4推荐阅读
- 📖 《Java 核心技术》卷 1 第 3.3 节 — 基本类型详解
- 🌐 Java 教程:基本数据类型 — Oracle 官方
- 📖 《深入理解 Java 虚拟机》第 2 章 — 字节码层面的类型
下一章:第 6 章:自动装箱与拆箱 →
你已经完成了:
- ✅ 掌握 8 种基本类型的字节数和取值范围
- ✅ 理解整数默认 int、浮点默认 double 的设计原因
- ✅ 了解字符编码演进(ASCII → Unicode → UTF-8)
- ✅ 掌握自动转换和强制转换规则
- ✅ 警惕基本类型的常见坑(精度损失、整数除法等)