前言
大家好!最近在系统性地梳理和重构后端知识体系。我深刻体会到:在深入研究各种高大上的框架和高并发架构之前,夯实 Java 底层基本功才是避坑的关键。这篇笔记总结了 JVM 的执行机制、核心数据类型以及常被误解的方法传参机制,希望能帮大家(也帮我自己)扫清进阶路上的底层盲区。
一、 JVM 与代码执行机制
1. 跨平台原理
- 核心概念:Java 语言是跨平台的,但 JVM 本身并不跨平台。
- 执行流程 :
javac编译器将.java源码编译成统一的字节码(.class 文件) 。 - 平台适配:不同的操作系统上安装不同版本的 JVM,由 JVM 负责将字节码翻译成当前操作系统能理解的本地机器码执行。
2. JDK、JRE 与 JVM 的包含关系
三者是严格的层级包含关系:JDK > JRE > JVM。
| 名称 | 英文全称 | 核心作用 | 包含内容 |
|---|---|---|---|
| JDK | Java Development Kit | 开发 Java 程序 | JRE + 开发调试工具(如 javac) |
| JRE | Java Runtime Environment | 运行 Java 程序 | JVM + 核心类库 |
| JVM | Java Virtual Machine | 执行 字节码 | GC、类加载器、JIT 编译器、解释器等 |
3. 解释与编译混合执行模式
Java 并不是纯粹的解释型或编译型语言,而是两者的结合:
- 解释器 :逐行解释字节码执行。优势是启动速度快,不需要等待整体编译。
- JIT 编译器 (Just-In-Time) :在程序运行过程中,识别出执行频率极高的"热点代码",将其直接编译成本地机器码并缓存。优势是后期执行速度极快。
二、 数据类型与核心机制
1. 八大基本数据类型
| 类型 | 字节数 | 默认值 | 核心考点与描述 |
|---|---|---|---|
byte |
1 | 0 | 最小整数,常用于底层文件或网络流处理。 |
short |
2 | 0 | 较少使用。 |
int |
4 | 0 | 最常用的整数类型(32位)。 |
long |
8 | 0L | 大整数(64位),赋值时需加 L 后缀。 |
float |
4 | 0.0f | 单精度浮点数。 |
double |
8 | 0.0 | 默认的浮点数类型。 |
char |
2 | '\u0000' | Unicode 字符。 |
boolean |
视JVM而定 | false | 仅取 true 或 false。 |
2. 类型转换与精度问题
- 转换规则 :小转大自动进行(如
int -> long);大转小需要强制转换(如long -> int),存在数据高位截断和溢出风险。 - 浮点数精度陷阱 :
double基于二进制浮点,无法精确表达 0.1 等十进制小数,会导致0.1 + 0.2 != 0.3。 - 解决方案 :凡是涉及金额、金融等对精度要求极高的场景,严禁使用 double ,必须使用
BigDecimal并传入字符串参数(如new BigDecimal("0.1"))。
3. Integer 包装类与缓存机制
-
存在的意义:支持泛型(集合不能存基本类型)、支持 null 值(适配数据库)、提供丰富的工具方法。
-
保留 int 的原因:性能更高(栈上直接分配)、省内存、不需要垃圾回收 (GC)。
-
缓存池陷阱 :
Integer默认缓存了 -128 ~ 127 范围内的对象。- 在此范围内自动装箱,复用同一个对象,
==比较为true。 - 超出此范围,每次都会在堆区
new新对象,==比较为false。
- 在此范围内自动装箱,复用同一个对象,
三、 对象的生命周期与内存管理
1. 创建对象的 5 种方式
new关键字:最常规、最常用的方式。- 反射 :
Constructor.newInstance()(推荐)或已过时的Class.newInstance()。 clone()方法 :直接内存克隆,不调用构造方法(需实现Cloneable接口)。- 反序列化:从网络或文件字节流中恢复对象,不调用构造方法。
- 工厂方法 :通过静态方法封装创建逻辑(如
Integer.valueOf())。
2. 垃圾回收 (GC) 基础
- 回收判断依据 :JVM 采用可达性分析算法。从 GC Roots 节点开始向下搜索寻找引用链,如果一个对象没有任何引用链相连(找不到),则判定为垃圾。
- 回收时机 :对象不可达不代表立即死亡,需要等待 GC 线程触发(通常在内存不足时)、到达安全点 (Safe Point) 等条件满足时才会真正回收。已废弃的
finalize()方法极度不可靠,不应被使用。
四、 方法参数传递机制(高频陷阱)
核心铁律:Java 中只有值传递 (Pass by Value),绝对没有引用传递!
-
基本类型传递 :传递的是具体数值的副本。在方法内部对形参的任何修改,都不会影响外部的实参变量。
-
引用类型传递 :传递的是引用地址的副本。
- 因为副本地址和外部原地址指向堆内存中的同一个对象,所以通过形参可以修改该对象的内部属性。
- 但如果直接将形参指向一个全新的对象(如
obj = new Object()),仅仅是改变了副本地址的指向,外部的原变量依然指向老对象,证明其本质仍是值传递。
结语
万丈高楼平地起,搞懂了 JVM 的运行机制和内存中的弯弯绕绕,我们在排查线上 OOM 或者分析代码逻辑时才能更有底气。
下一篇,我将继续更新《重塑 OOP 思维:面向对象核心原则与 String 底层深度解剖》,带大家彻底搞透面试必问的 String 常量池机制与 intern() 的底层陷阱。欢迎点赞关注,我们下期见!