时间概念没建立:
❌ 写代码时
❌ 编译时
❌ 运行时
这三个在脑子里是“糊在一起的”。
今天这条,我只干一件事:
把这三个时间点,用“人能理解的方式”彻底分开。
不讲 Spring,不讲 MapStruct,不讲 IOC。
先把“时间轴”立起来。
一、先给你一句“总纲版结论”(先记住)
Java 程序一生分 4 个阶段:
1️⃣ 写代码时(你在敲代码)
2️⃣ 编译时(javac 在干活)
3️⃣ 启动时(JVM + 框架启动)
4️⃣ 运行时(程序正在跑)
你现在最大的问题是:把 1、2、3、4 当成一坨了
二、第一阶段:什么叫「写代码时」?
1️⃣ 写代码时 = 你坐在电脑前敲字
public class User { private String name; }这个阶段的特点:
- ❌ 程序还没运行
- ❌ JVM 还没启动
- ❌ Spring 完全不存在
- ❌ MapStruct 不存在
- ❌ 反射不存在
📌这只是“文本文件”
就像你在 Word 里打字一样。
2️⃣ 写代码时你能做什么?
你只能:
- 写类
- 写方法
- 写注解
- 写 if / for
你不能:
- new 一个对象(还没运行)
- 调一个方法
- 注入一个 Bean
3️⃣ 非常关键的一句话
写代码时,你是在“描述未来会发生什么”
但事情还没发生
三、第二阶段:什么叫「编译时」?
4️⃣ 编译时 = javac 开始工作
你点了:
Run / Build / mvn package这时发生:
.java → javac → .class5️⃣ 编译时 JVM 启动了吗?
❌没有
这点非常非常重要。
编译时:
- ❌ 没有 main 方法执行
- ❌ 没有 Spring Boot 启动
- ❌ 没有数据库连接
- ❌ 没有对象实例
6️⃣ 编译时 javac 在干什么?
主要干 3 件事:
① 语法检查
int a = "hello"; ❌直接报错
② 类型检查
User u = new Order(); ❌编译不过
③注解处理器(超级重要)
MapStruct、Lombok 就在这一步出现
7️⃣ MapStruct 发生在这里(关键)
@Mapper public interface WorkOrderConverter { }编译时:
- javac 发现 @Mapper
- MapStruct 的注解处理器被触发
- 生成:
WorkOrderConverterImpl.java📌注意:此时程序还没运行
8️⃣ 一句话总结编译时
编译时 = 把你写的代码“加工成能运行的代码”
四、第三阶段:什么叫「启动时」?
9️⃣ 启动时 = JVM 启动 + 框架初始化
你点了:
Run Application发生:
JVM 启动 ↓ 加载 .class ↓ 执行 main 方法🔟 Spring Boot 的启动就在这里
public static void main(String[] args) { SpringApplication.run(App.class, args); }📌Spring IOC 容器是在“启动时”创建的
1️⃣ 1️⃣启动时 Spring 在干什么?
(这个需要以后慢慢啃的)
- 扫描 classpath
- 找 @Component / @Service / @Mapper
- new 对象
- 放进 IOC 容器
- 建立依赖关系
⚠️ 这一步不是编译时
五、第四阶段:什么叫「运行时」?
1️⃣2️⃣ 运行时 = 程序已经启动,正在服务用户
例如:
- 你访问接口
- Controller 方法被调用
- Service 逻辑执行
- DAO 操作数据库
1️⃣3️⃣ 反射发生在这里(重点)
clazz.getDeclaredConstructor().newInstance();👉反射一定发生在运行时
原因很简单:
编译时你不知道是哪个类
只有运行时才能知道
六、把四个阶段放在一条“时间轴”上
你敲代码 ↓ 写代码时 ↓ javac ↓ 编译时(MapStruct / Lombok) ↓ java -jar ↓ 启动时(Spring IOC 创建对象) ↓ 接口请求 ↓ 运行时(反射 / 业务逻辑)七、现在回到最困惑的那一行
clazz.getDeclaredConstructor().newInstance();1️⃣4️⃣ 它发生在什么时候?
答案非常明确:
✅运行时
1️⃣5️⃣ 为什么不能在编译时 new?
因为编译时:
- ❌ 不知道用户项目里有什么类
- ❌ 不知道哪些类加了 @Component
- ❌ 不知道依赖关系
八、什么是 JVM?(先给“能用的理解”)
你现在不用理解 JVM 内存模型,只要知道一句话:
JVM = 执行 Java 字节码的“虚拟电脑”
它负责:
- 加载 class
- 创建对象
- 执行方法
- 回收内存
九、你现在应该“突然通了一点点”
你现在至少应该能区分:
阶段 | 能干什么 |
写代码时 | 写 class、注解 |
编译时 | 生成 class、生成代码 |
启动时 | Spring 创建对象 |
运行时 | 调方法、反射 |
问题:为啥会存在编译时,如果没有点击编译能不能直接运行?
一、先回答这个最核心的问题
“为啥会存在编译时?如果我没有点击编译,能不能直接运行?”
结论先给你一句100% 正确的话:
Java 不能不编译就运行
你所谓的“没点编译就能跑”,只是工具帮你偷偷编译了
二、从「人类」和「计算机」的矛盾开始讲
1️⃣ 计算机根本不认识你写的 Java
你写的代码:
System.out.println("Hello World");在你眼里是:
打印一句话
但在计算机眼里是:
❌ 完全不认识
计算机只认识什么?
👉二进制
01001001 00101010 10101010 ...所以问题来了:
❓ 那 Java 是怎么让计算机执行的?
答案就是两个字:
翻译
三、什么是「编译」?(非常重要)
1️⃣ 编译 = 翻译
编译器 = 翻译官
人类世界 | 计算机世界 |
Java 源代码 | 二进制指令 |
人写的 | 机器执行的 |
|
|
举个生活例子(一定要看)
你是中国人,去德国:
- 你说:中文
- 德国人听不懂
- 必须有一个翻译
Java 世界完全一样:
你写 Java → JVM 听不懂 ↓ 编译器翻译 ↓ JVM 才能执行2️⃣ Java 的「编译产物」是什么?
不是直接变成机器指令,而是:
字节码(bytecode)
.java →(javac 编译)→ .class.class文件 ≠ 机器码.class文件 =JVM 能看懂的“中间语言”
四、那 JVM 又是干嘛的?
1️⃣ JVM 是一个「虚拟计算机」
它干的事只有一件:
读取.class文件,并执行
所以完整链路是:
你写代码(.java) ↓ 编译(javac) ↓ .class 文件 ↓ JVM 执行2️⃣ 为什么 Java 要多这一层?
因为:
一次编译,到处运行
- Windows JVM
- Linux JVM
- Mac JVM
只要 JVM 存在,.class就能跑
五、那“我没点编译,程序咋就跑了?”
这是99% 新人都会误解的地方。
真相是:
你“没有看到编译”,≠ “没有发生编译”
场景 1:IDEA 里点「Run」
IDEA 实际做了什么?
1. 自动保存代码 2. 自动调用 javac 编译 3. 自动启动 JVM 4. 把 .class 交给 JVM 执行👉只是你没看见
场景 2:Spring Boot 启动
你点了:
Run Application背后发生的是:
maven compile ↓ javac ↓ 生成 .class ↓ java -jar 启动 JVM场景 3:命令行运行
你必须手动:
javac Main.java java Main❗ 所以回答你那句话:
如果我没有点击编译能不能直接运行?
👉不能
只是:
IDE / Maven / Gradle 替你做了编译
六、什么是「写代码时 / 编译时 / 运行时」?
这是你后面理解IOC / 反射 / MapStruct / 注解处理器的钥匙。
1️⃣ 写代码时(Coding Time)
👉你在 IDEA 里敲代码
.java- 没有任何执行
- 只是文本
📌 此时:
- Spring 没启动
- JVM 没启动
- 程序根本不存在
2️⃣ 编译时(Compile Time)
👉javac 正在干活
它做什么?
- 检查语法
- 检查类型
- 处理注解(非常重要⚠️)
- 生成
.class
📌MapStruct 就在这一步工作
3️⃣ 运行时(Runtime)
👉JVM 启动后
- Spring IOC 在这里
- Bean 创建在这里
- 反射发生在这里
- 方法调用在这里
用一句话记住:
阶段 | 是否有 JVM |
写代码 | ❌ |
编译时 | ❌ |
运行时 | ✅ |
七、为什么非要有「编译时」?
你问的是一个非常本质的问题。
如果没有编译会怎样?
想象一下:
- JVM 每执行一行 Java
- 现场分析语法
- 现场分析类型
- 现场找错误
👉慢到爆炸
编译的本质价值:
把“复杂的检查和准备”,提前一次性做完
这样运行时:
- 不用检查语法
- 不用分析结构
- 直接执行
八、这和 MapStruct 有什么关系?
MapStruct 的核心一句话
MapStruct 是“编译时”生成代码的工具
它不是运行时反射!
MapStruct 在哪一步干活?
👉编译时
javac 编译 ↓ 发现 @Mapper ↓ MapStruct 注解处理器介入 ↓ 生成 XxxMapperImpl.java ↓ 一起编译成 .class这就是为什么:
- MapStruct性能高
- 没有反射
- 调用是普通方法调用
IDEA 到底在偷偷干了什么
一、先给你一句“不绕弯”的结论
不是你写一行,IDEA 就真的帮你“编译一行”
但 IDEA 在“模拟编译器的工作”,实时帮你做检查
换句话说:
IDEA ≠ JVM
IDEA ≠ 真正的 javac
IDEA = 非常聪明的“语法/语义检查器”
二、你现在看到的“红色报错”到底是谁给的?
我们拆成三层来讲:
1️⃣ 你敲代码的那一刻,发生了什么?
你在 IDEA 里写:
int a = "hello";你还没点 Run
你还没点 Compile
你甚至还没保存
👉但是已经红了
关键点:
❗此时,根本没有发生真正的“Java 编译”
2️⃣ 那是谁在报错?
👉是 IDEA 自己在报错
IDEA 内部有一套:
- Java 语法解析器
- 类型推导系统
- 代码分析引擎(PSI)
它在做的是:
“假装自己是编译器,提前帮你看一眼”
用一句人话说:
IDEA 在你写代码时,一直在心里嘀咕:
“如果现在拿去给 javac 编译,会不会炸?”
3️⃣ 所以 IDEA 在做的事是:
行为 | 是否是真编译 |
红色波浪线 | ❌ |
黄色警告 | ❌ |
自动补全 | ❌ |
Ctrl + B 跳转 | ❌ |
提示类型不匹配 | ❌ |
👉全都不是 javac
三、那什么才叫「真正的编译」?
只有这几种情况之一,才叫真正编译:
✅ 情况 1:你点了「Build / Compile」
Build → Build Project✅ 情况 2:你点了「Run」
IDEA 自动帮你做了:
javac 编译 ↓ 生成 .class ↓ 启动 JVM✅ 情况 3:你用 Maven / Gradle
mvn compile mvn package⚠️ 核心区分点
有没有生成.class文件
- 有
.class→ 真编译 - 没
.class→ 都是假象
四、那 IDEA 为啥能“像编译器一样聪明”?
这是一个非常关键的认知。
1️⃣ IDEA 用的是「静态代码分析」
什么叫静态?
👉代码不运行,只看结构
它做的事情包括:
- 解析语法树(AST)
- 推断变量类型
- 检查方法是否存在
- 检查泛型是否匹配
举例:
List<String> list = new ArrayList<>(); list.add(123);IDEA 能立刻告诉你:
❌ 你加了 Integer,但 List 里是 String
2️⃣ 但 IDEA 做不到什么?
它做不到:
Object obj = getSomething(); obj.doSomething();如果:
getSomething()是运行时才决定的- 用了反射
- 用了泛型擦除后的类型
👉 IDEA可能不报错,但运行时会炸
3️⃣ 所以你要记住一句话(非常重要)
IDEA 只管“像不像能编译”
javac 决定“能不能编译”
JVM 决定“能不能跑”
五、那为什么“写一半代码也能红”?
因为 IDEA 在做:
“不完整代码的容错分析”
例如你刚写:
public class A { public void test(这是不完整代码
javac 一定会报错
但 IDEA 仍然:
- 尽量帮你解析
- 尽量给你补全
- 尽量提示你下一步
六、所以现在我们把“时间轴”重新画一遍
你在 IDEA 写代码 ↓ IDEA 实时分析(假编译) ↓ 你点击 Run / Build ↓ 真正 javac 编译(编译时) ↓ 生成 .class ↓ JVM 启动(运行时)