导读:这是一篇适合公众号和博客发布的原创改写版上篇,重点不是堆概念,而是帮你把 Java 基础题背后的逻辑顺序理顺。你如果能把这一篇讲顺,很多“八股”题都会自然收束到几个核心框架里。
系列阅读:Java 基础常见知识点与面试题总结(中) | Java 基础常见知识点与面试题总结(下)
一、先把 Java 的运行全景图讲清楚
很多人一上来就背语法、背 API,结果一到面试就容易碎。更稳的方式是先把 Java 的运行链路讲明白:源码是怎么变成可执行结果的,JVM、JDK、JRE 分别处在什么位置,为什么大家总说 Java 是“编译与解释并存”的语言。
| 概念 | 你可以怎么理解 | 主要作用 |
|---|---|---|
| JVM | Java 程序的运行引擎 | 负责加载字节码并执行 |
| JRE | 传统意义上的运行环境 | 提供 JVM 和基础类库 |
| JDK | 开发工具包 | 在 JRE 基础上再提供编译、调试、文档等工具 |
面试里最常见的回答方式是:JVM 负责运行,JDK 负责开发,JRE 是传统概念下的运行时环境。如果你再补一句“从 JDK 9 开始,JRE 的边界已经不像早期版本那样单独强调”,通常就够了。
二、为什么 Java 既像编译型语言,又像解释型语言
Java 源码不会直接变成平台相关的机器码,而是先经过 javac 编译成字节码文件,再交给 JVM 执行。JVM 在执行过程中,一部分代码会被解释执行,热点代码则会被 JIT 编译成机器码。因此,说 Java 既有编译型语言的特征,也有解释型语言的特征,是成立的。
- 写出 .java 源码。
- 使用 javac 编译成 .class 字节码。
- JVM 加载字节码。
- 解释器先执行,JIT 再把热点代码编译成更高效的机器码。
如果面试官继续追问 JIT 和 AOT 的区别,你可以这样答:JIT 更适合长时间运行的服务,AOT 更适合启动速度和内存占用更敏感的场景。JIT 启动有预热成本,但峰值性能通常更好;AOT 启动更快,不过对反射、动态代理这类动态特性的支持会更受限。
三、基础语法题为什么总爱考这些
语法题看起来琐碎,实际上考的是你对执行顺序和语言规则有没有稳定认知。常见高频点主要集中在下面这几类:
- 注释:单行注释、多行注释、文档注释。真正重要的不是背分类,而是知道注释应该解释“为什么”,而不是把代码翻译一遍。
- 关键字:全部小写,不能用作标识符。像 default 这种关键字在不同上下文里还有不同语义。
- 自增自减:前置先变后用,后置先用后变。
- 流程控制:continue 结束本轮循环,break 跳出整个循环,return 结束整个方法。
- 移位运算:<< 左移,>> 带符号右移,>>> 无符号右移。
int a = 9;
int b = a++;
int c = ++a;
// 执行后:a = 11, b = 9, c = 11
提醒:语法题最怕“感觉上会”。像移位、自增和 return/finally 这种题,只靠印象很容易翻车,最好自己手推两三遍。
四、8 种基本数据类型,别只停留在会背名字
byte、short、int、long、float、double、char、boolean 这 8 种基本类型谁都会背,但面试真正喜欢问的是三个延伸点:默认值、字面量规则和包装类差异。
| 类型 | 位数 | 默认值 | 记忆点 |
|---|---|---|---|
| byte | 8 | 0 | 最小整数类型 |
| short | 16 | 0 | 较少单独使用 |
| int | 32 | 0 | 整数默认主力类型 |
| long | 64 | 0L | 字面量建议加 L |
| float | 32 | 0.0f | 字面量必须加 f 或 F |
| double | 64 | 0.0d | 浮点默认主力类型 |
| char | 16 | \u0000 | 单引号,且占 2 字节 |
| boolean | 实现相关 | false | 逻辑值只有 true 和 false |
包装类方面,最容易被忽略的是:包装类型能表示 null,也能用于泛型,而基本类型不行。这就是为什么对象属性、集合元素、框架返回值里更常看到包装类型。
五、包装类缓存和自动装箱,是基础题里的经典陷阱
Integer、Long、Short、Byte 等包装类为了减少对象重复创建,会对一部分小范围数值做缓存。最常见的结论是:Integer 默认缓存 -128 到 127。所以你看到下面这种代码时,别只凭肉眼判断。
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c == d); // false
System.out.println(c.equals(d)); // true
自动装箱本质是调用 valueOf,自动拆箱本质是调用 intValue、longValue 这类方法。语法是省事了,但在高频循环中反复拆装箱会带来额外开销,所以数值密集计算仍然优先考虑基本类型。
六、浮点数为什么不精确,业务里该怎么处理
很多十进制小数都无法用有限位的二进制精确表示,所以 float 和 double 做运算时会出现精度损失。这不是 Java 特有的问题,而是二进制浮点表示本身的限制。
所以只要场景涉及金额、费率、分账、对账,最稳的选择就是 BigDecimal。并且要养成两个习惯:
- 使用字符串构造 BigDecimal,避免把二进制误差先带进来。
- 比较数值大小时优先使用 compareTo,而不是直接依赖 equals。
BigDecimal a = new BigDecimal("1.00");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.equals(b)); // false
System.out.println(a.compareTo(b) == 0); // true
七、变量、static 和方法题,考的是边界感
成员变量和局部变量的区别,几乎每个 Java 面试都会出现。更准确的回答方式不是只背“一个在堆一个在栈”,而是结合归属、生命周期、默认值一起讲:
- 成员变量属于对象或类,局部变量属于方法或代码块。
- 成员变量可以有默认值,局部变量必须显式初始化。
- static 修饰的变量和方法属于类,不依赖具体实例。
因此,静态方法不能直接访问非静态成员,本质原因就是:类级别的逻辑执行时,实例级别的状态未必存在。
| 对比项 | 重载 | 重写 |
|---|---|---|
| 发生位置 | 同一个类中 | 父子类之间 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 必须不同 | 必须相同 |
| 绑定时机 | 编译期 | 运行期 |
如果想把重写说得更像工程实践,可以再补一句:子类重写父类方法时,返回值范围、异常范围和访问权限都有约束,不是想改就能改。
八、上篇最适合面试前复盘的答题顺序
如果你明天就要面试,上篇建议按下面顺序复盘:
- JVM、JDK、JRE 和字节码执行链路。
- Java 为什么说编译与解释并存,JIT 和 AOT 的区别。
- 8 种基本数据类型和包装类差异。
- 自动装箱拆箱、包装类缓存、浮点精度、BigDecimal。
- 成员变量与局部变量、static、重载与重写、可变参数。
一句话总结:上篇真正要建立的是 Java 的“底层运行感”。只要这个框架稳了,后面很多知识点都不再是孤立的背诵题。