按知识章节整理的学习笔记,适合 Java 零基础阶段对照阅读。本篇对应「数据类型与变量」这一章。<br>每个知识点尽量回答「为什么」,而不是只给结论。
一、为什么非得有数据类型
C 语言里 int a 和 double a 的区别是"占多少字节",Java 也一样,但 Java 走得更绝一些:它的每种基本类型,字节数是被规范写死的,不随操作系统变。
比如 C 里 int 在 16 位机器上是 2 字节,32 位机器上是 4 字节。写个程序在这台机器上能跑,换台机器可能就溢出。
Java 的取舍是:用固定的字节数,换"一次编译,到处运行"。int 永远是 4 字节,不管是 Windows、Linux 还是 ARM 架构的服务器。
二、八种基本类型全表
| 类型 | 字节 | 包装类 | 范围 | 日常用不用 |
|---|---|---|---|---|
byte | 1 | Byte | -128 ~ 127 | 少见(IO、数组省内存) |
short | 2 | Short | -32768 ~ 32767 | 几乎不用 |
int | 4 | Integer | -2147483648 ~ 2147483647 | 最常用 |
long | 8 | Long | ±9.2×10¹⁸ | 时间戳、大数 |
float | 4 | Float | 约 ±3.4×10³⁸ | 少见 |
double | 8 | Double | 约 ±1.7×10³⁰⁸ | 小数默认 |
char | 2 | Character | 0 ~ 65535 | 字符处理 |
boolean | 未规定 | Boolean | true/false | 逻辑判断 |
记忆抓手:包装类的命名规律
八种基本类型对应八个包装类,只有两个是"改名"的:
int→Integer(不是Int)char→Character(不是Char)
其余六个都是首字母大写。所以看到 Long.MAX_VALUE、Byte.MIN_VALUE 都能直觉理解。
为什么要记包装类? 因为基本类型本身没有任何方法。想知道 int 的最大值,写 int.MAX_VALUE 是错的(编译不过),必须写 Integer.MAX_VALUE——值挂在包装类上,不在基本类型上。
三个容易记错的点
① char 是 2 字节,能存汉字
char ch1 = 'a'; // 英文字母
char ch2 = '高'; // 汉字,合法
这跟 C 不一样(C 的 char 是 1 字节,存不下汉字)。Java 用 Unicode,char 是 2 字节。
但有个坑:2 字节只够表示基本平面(BMP)的字符,emoji 需要两个 char。
System.out.println("高".length()); // 1
System.out.println("😀".length()); // 2 ← 一个表情,长度是 2
因为 emoji 超出 65535 的范围,要用"代理对"(surrogate pair)两个 char 拼起来。这个细节在后续处理文本时会踩坑,现在知道有这回事就行。
② boolean 的字节数,规范没规定
Java 规范只规定了 true/false 两个取值,没规定占多少内存。JVM 实现里通常用 1 个字节(或者用 int 的 0/1 表示),但这不属于语言规范。
面试问"boolean 占几个字节",标准答案是"规范未明确规定,取决于 JVM 实现"。答"1 字节"其实不严谨。
③ Java 里没有"非 0 即真"
if (1) { } // ❌ 编译错误:不兼容的类型,int 无法转换为 boolean
C 语言里 if (1) 是对的,Java 直接禁止。条件必须是 boolean 类型——这是 Java 强类型的一个体现,也避免了 if (a = 1) 这种把赋值写成判断的低级错误(Java 里这个也是编译错误,因为 a = 1 的结果是 int)。
三、float 后面那个 F,以及 long 的 L
float f = 10.5F; // ✅
float f = 10.5; // ❌ 不兼容的类型:从 double 转换到 float 可能会有损失
Java 里的小数字面量默认是 double。 10.5 这个字面量从出生就是 double(8 字节),往 float(4 字节)里放属于窄化转换,编译器不敢替你做这个决定。
加 F 就是明确告诉编译器"这个字面量我要它当 float"。
long 的情况类似但更松一些:整数字面量默认是 int,只要不超过 int 范围,long l = 10; 直接写就行。但超过就必须加 L:
long big = 3000000000; // ❌ 整数过大,这个数字超出了 int 的范围
long big = 3000000000L; // ✅
注意 L 要写大写。 小写 l 和数字 1 在某些字体里长得一模一样,1l 看着就像 11。
四、类型提升:一个能让人卡住半小时的报错
byte d = 10;
byte e = 10;
byte f = d + e; // ❌ 不兼容的类型:从 int 转换到 byte 可能会有损失
两个 byte 加起来,怎么就成了 int?
规则是二元数值提升:参与算术运算时,如果两个操作数都不是 long/float/double,就都提升为 int。
byte + byte → 提升为 int + int → 结果是 int
所以把 int 塞回 byte 变量,编译器要拦。两种改法:
int f = d + e; // 用 int 接
byte f = (byte) (d + e); // 强转,括号必须包住整个表达式
注意括号不能省:
byte f = (byte) d + e; // ❌ 还是报错,只转了 d,后面 + e 又提升回 int
同样的规则,换个方向也一样:
int a = 10;
long b = 20;
long c = a + b; // int + long → long,结果必须是 long 接
int c = a + b; // ❌ 装不下
一句话记住:运算结果的类型,取参与运算里"最大"的那个。 顺序是 byte → short → int → long → float → double。
设计动机值得深究:Java 之所以要把 byte 拉到 int 再运算,是因为 CPU 的整数运算单元本身就是按 32 位设计的,没有高效的 8 位加法指令。运算统一拉到 int 这个"原生宽度",既符合硬件现实,也让字节码更简单。
同一个规则还有两个延伸坑:
// ① 复合赋值隐含强制类型转换
byte b = 10;
b = b + 1; // ❌ 报错
b += 1; // ✅ 合法,等价于 b = (byte)(b + 1)
// ② 但它会静默溢出
byte b2 = 127;
b2 += 1;
System.out.println(b2); // -128,编译器一句警告都没有
报错的代码会被改掉,不报错的代码可能直接上线——这是类型提升这个话题里最需要警惕的地方。
五、String 到底算不算一种"类型"
初学数据类型时,很容易把 String 当作第九种基本类型。但它不是基本类型,它是类。
String str = "hello"; // 这是创建了一个对象,不是基本类型的赋值
证据有几个:
- 首字母大写 —— Java 里所有类名都是大驼峰,基本类型全是小写(
int、char)。String大写就说明它是类。 - 它是引用类型 —— 变量里存的不是字符串本身,而是指向字符串对象的地址。
- 它有方法 —— 基本类型不能调方法,
str.length()能调,因为它是对象。 - 它没有对应的包装类 —— 因为它本身就是类,不需要包装。
这个区分为什么重要? 因为后面学集合、泛型、比较的时候,基本类型和引用类型的差别会变得非常关键。比如 == 比较 String 时比的是地址而不是内容(要用 equals),而 int 用 == 比的就是值。现在把 String 归对位置,后面就不容易乱。
六、字符串拼接:那个 + 号的执行顺序
int a = 10;
int b = 20;
System.out.println("a=" + a + b); // a=1020
System.out.println("a=" + (a + b)); // a=30
System.out.println(a + b + "=a+c"); // 30=a+c
三行看着差不多,结果完全不同。原因是 + 从左到右执行,而它有两种含义:数值加法、字符串拼接。
- 第一行:先算
"a=" + a→ 结果已经是字符串"a=10",再+ b只能拼接 →"a=1020" - 第二行:括号优先,
a + b = 30先算出来,再拼接 →"a=30" - 第三行:
a + b两个都是 int,走算术加法 →30,再和字符串拼 →"30=a+c"
规律:一旦 + 的左边出现了 String,后面所有的 + 都变成拼接。
顺便说一句:
+在 Java 里能拼字符串,是编译器特批的。Java 并不支持运算符重载(除了这一个特例),这也是为什么 Java 里没有自定义的运算符重载。
七、字符串和数值互转
数值 → 字符串
int a = 123;
String s1 = a + ""; // 最偷懒的写法
String s2 = String.valueOf(a); // 推荐,意图明确
a + "" 能用,但意图不清晰而且性能稍差。实际开发一律用 String.valueOf(),或者 Integer.toString(a)。
字符串 → 数值
String s = "123";
int b1 = Integer.parseInt(s); // 返回 int
int b2 = Integer.valueOf(s); // 返回 Integer,自动拆箱后看着一样
很多笔记会写"这两个一样",严格说不一样:
parseInt返回int(基本类型)valueOf返回Integer(包装类对象)
在新版 JDK 里 valueOf 内部就是调 parseInt 再装箱,日常用起来没区别,但面试可能会问。而且异常行为要注意:
String s = "abc";
Integer.parseInt(s); // NumberFormatException
String s2 = null;
Integer.parseInt(s2); // NumberFormatException(不是空指针)
只要不是合法数字(包括 null),都抛 NumberFormatException。
八、本章踩坑记录
- 以为
String是第九种基本类型,实际它是类(首字母大写就是线索) - 以为
boolean占 1 字节,实际规范没规定 - 以为
Integer.valueOf()和parseInt()完全一样,返回值类型其实不同 char存汉字没问题,但 emoji 要占 2 个 charfloat f = 10.5;不加 F 会报错,long big = 3000000000;不加 L 也会报错byte b = 127; b += 1;结果是 -128,编译器不报错
九、自测
int的最大值怎么打印?为什么不能写int.MAX_VALUE?"😀".length()等于几?为什么?byte a = 1, b = 2; byte c = a + b;报错原因?写出两种改法。String.valueOf(1)和1 + ""有区别吗?Integer.parseInt(null)会抛什么异常?System.out.println(1 + 2 + "3" + 4 + 5);输出什么?boolean到底占几个字节?
下一篇:运算符——从「5 / 2 为什么等于 2」说起,包括自增自减、短路逻辑、位运算。
本文系统讲解Java八种基本数据类型,包括其字节大小、包装类命名规律及易错点(如char支持汉字但emoji需代理对、boolean无规范字节数、无隐式布尔转换);深入剖析类型提升机制(byte运算自动升为int)、float/double字面量后缀规则、String作为引用类型而非基本类型的本质,以及+号在字符串拼接中的左结合性与类型推导逻辑;最后涵盖字符串与数值互转方法及典型踩坑场景。
5711

被折叠的 条评论
为什么被折叠?



