编辑
2026-03-20
后端-Java
00

目录

常见误解:是不是Maven用哪个版本的JDK,编译出来的Java应用也是这个版本的?
核心概念
实际示例
场景一:Maven 用 JDK 21,编译出 JDK 8 的 JAR
场景二:Maven 用 JDK 8,编译出 JDK 8 的 JAR
配置对比
1. 默认行为(未配置时)
2. 明确配置目标版本
完整示例演示
pom.xml 配置
Docker 环境测试
验证脚本
字节码版本对照表
实际应用场景
场景 1:开发环境使用高版本 JDK,生产环境使用低版本
场景 2:多版本编译
重要限制
⚠️ 不能反向编译
⚠️ API 兼容性
总结

本文针对“Maven 用哪个版本 JDK,编译出的应用就是哪个版本”这一常见误解进行深入剖析,系统阐述 Maven 编译过程中两个核心 JDK 概念的差异:

  1. 运行 JDK(Runtime JDK):执行 Maven 命令本身使用的 JDK 版本
  2. 目标 JDK(Target JDK):编译出的字节码要运行的 JDK 版本

核心内容包括:

  • 配置对比:对比默认行为(未配置时 Maven 使用运行 JDK 版本作为目标版本)与明确配置目标版本(通过 maven.compiler.release 固定输出字节码版本)的差异

  • 关键区分:详细对比 maven.compiler.releasejava.version 两个配置项的作用对象、配置方式、影响范围及典型值示例,澄清前者控制编译目标、后者控制运行环境的本质区别

  • 实战验证:提供完整的测试项目代码(Test.java + pom.xml)、Docker 环境测试方案及验证脚本,读者可自行运行验证

  • 字节码对照:附 JDK 8/11/17/21 对应的 Major Version 与编译参数速查表

  • 重要限制:强调不能反向编译(低版本 JDK 无法编译高版本字节码)及 API 兼容性约束(即使字节码版本正确,也不能使用高版本 API)

通过本文,读者将理解 Maven 跨版本编译的原理,掌握让高版本 JDK 编译出低版本兼容字节码的配置方法,实现开发环境使用最新 JDK 特性、生产环境保持低版本 JDK 兼容性的灵活构建策略。

常见误解:是不是Maven用哪个版本的JDK,编译出来的Java应用也是这个版本的?

不是的! Maven 使用的 JDK 版本和编译出的应用目标版本可以不同。

核心概念

Maven 编译过程涉及两个 JDK 概念:

  1. 运行 JDK(Runtime JDK):执行 Maven 命令本身使用的 JDK
  2. 目标 JDK(Target JDK):编译出的字节码要运行的 JDK 版本

它们可以完全独立配置。

实际示例

场景一:Maven 用 JDK 21,编译出 JDK 8 的 JAR

Bash
# Maven 运行在 JDK 21 上 $ mvn -version Apache Maven 3.9.14 Java version: 21.0.1, vendor: Oracle Corporation # 但通过配置,可以编译出 JDK 8 兼容的字节码 $ mvn clean package -Dmaven.compiler.release=8 # 检查字节码版本(major version 52 = JDK 8) $ javap -verbose target/classes/MyClass.class | grep "major" major version: 52 # 这是 JDK 8

场景二:Maven 用 JDK 8,编译出 JDK 8 的 JAR

Bash
# Maven 运行在 JDK 8 上 $ mvn -version Java version: 1.8.0_392 # 默认编译出 JDK 8 字节码 $ mvn clean package # 检查字节码版本 $ javap -verbose target/classes/MyClass.class | grep "major" major version: 52 # JDK 8

配置对比

1. 默认行为(未配置时)

XML
<!-- 未配置编译器版本时 --> <project> <!-- 没有任何 compiler 配置 --> </project>

结果:Maven 使用运行 JDK 的版本作为目标版本

  • 运行 JDK 21 → 编译出 JDK 21 字节码(major version 65)
  • 运行 JDK 8 → 编译出 JDK 8 字节码(major version 52)

2. 明确配置目标版本

XML
<project> <properties> <!-- 明确指定目标版本 --> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <!-- 或使用 release(推荐) --> <maven.compiler.release>8</maven.compiler.release> </properties> </project>

结果:无论运行 JDK 是什么版本,都编译出 JDK 8 字节码

  • 运行 JDK 21 → 编译出 JDK 8 字节码 ✓
  • 运行 JDK 8 → 编译出 JDK 8 字节码 ✓

Tip. 如果配置是这样的

XML
<project> <properties> <!-- 这里指定 Maven 进程本身(以及它调用的编译器) --> <java.version>8</java.version> </properties> </project>
  • maven.compiler.release 控制「编译出的字节码要跑在哪个 Java 版本上」
  • java.version 控制「运行 Maven 本身用的 JDK 版本」。
  • 前者是编译目标,后者是运行环境
对比维度maven.compiler.releasejava.version
作用对象编译出的 .class 字节码Maven 进程本身(以及它调用的编译器)
定义位置pom.xml<properties>系统环境变量或 JAVA_HOME
配置方式Maven 项目配置命令行 export JAVA_HOME=... 或 IDE 设置
影响范围仅影响当前项目的编译输出影响整个 Maven 执行过程及所有插件
典型值示例8, 11, 17, 211.8, 11, 17, 21(注意格式差异)
是否可跨版本✅ 可以用高版本 JDK 编译低版本目标❌ 只能是实际安装的 JDK 版本
控制字节码版本✅ 是❌ 否(仅决定运行环境)
限制 API 使用✅ 是(--release 会限制只能使用目标版本的 API)❌ 否
限制语言特性✅ 是(如 switch 表达式、文本块等)❌ 否
默认值无(不配置时等于运行 JDK 的版本)当前运行 Maven 的 JDK 实际版本
能否被命令行覆盖✅ 可以:-Dmaven.compiler.release=8❌ 不能(需改 JAVA_HOME 环境变量)
与 Maven 版本的关系需要 Maven 3.6.0+ 或 Compiler Plugin 3.6.0+与 Maven 版本无关
配置错误的后果编译失败或字节码版本不匹配Maven 无法运行或插件不兼容

完整示例演示

创建一个测试项目来验证:

Java
// src/main/java/Test.java public class Test { public static void main(String[] args) { System.out.println("Java version: " + System.getProperty("java.version")); System.out.println("Class file version: " + getClassFileVersion()); } private static String getClassFileVersion() { // 通过字节码判断版本 String version = System.getProperty("java.class.version"); return version; } }

pom.xml 配置

XML
<project> <modelVersion>4.0.0</modelVersion> <groupId>cn.odboy</groupId> <artifactId>cutejava</artifactId> <version>1.0.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <!-- 目标 JDK 8 --> <maven.compiler.release>8</maven.compiler.release> </properties> <build> <!-- 和应用名一致 --> <finalName>cutejava</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- 跳过单元测试 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build> </project>

Docker 环境测试

Docker
# Dockerfile - 使用 JDK 21 运行 Maven FROM eclipse-temurin:21-jdk RUN apt-get update && apt-get install -y maven WORKDIR /app COPY pom.xml . COPY src ./src # Maven 运行在 JDK 21 上 RUN mvn -version RUN mvn clean package # 检查字节码版本 RUN javap -verbose target/classes/Test.class | grep "major"

验证脚本

创建一个验证脚本来演示不同组合:

Bash
#!/bin/bash # verify-jdk-compatibility.sh echo "=== 测试 1: JDK 21 编译 JDK 8 目标 ===" docker run --rm -v $(pwd):/app -w /app \ eclipse-temurin:21-jdk \ sh -c "mvn clean compile -Dmaven.compiler.release=8 && \ javap -verbose target/classes/Test.class | grep 'major'" echo "" echo "=== 测试 2: JDK 8 编译 JDK 8 目标 ===" docker run --rm -v $(pwd):/app -w /app \ eclipse-temurin:8-jdk \ sh -c "mvn clean compile -Dmaven.compiler.release=8 && \ javap -verbose target/classes/Test.class | grep 'major'" echo "" echo "=== 测试 3: JDK 21 默认编译(无配置)===" docker run --rm -v $(pwd):/app -w /app \ eclipse-temurin:21-jdk \ sh -c "mvn clean compile && \ javap -verbose target/classes/Test.class | grep 'major'"

字节码版本对照表

目标 JDKMajor Version编译参数
JDK 852-source 1.8 -target 1.8--release 8
JDK 1155--release 11
JDK 1761--release 17
JDK 2165--release 21

实际应用场景

场景 1:开发环境使用高版本 JDK,生产环境使用低版本

YAML
build: image: maven:3.9.14-eclipse-temurin-21 # 使用 JDK 21 构建 script: # 但目标是 JDK 8 环境 - mvn clean package -Dmaven.compiler.release=8 artifacts: paths: - target/*.jar deploy: image: openjdk:8-jre # 生产环境用 JDK 8 运行 script: - java -jar target/*.jar

场景 2:多版本编译

Bash
# 编译多个目标版本 mvn clean package -Dmaven.compiler.release=8 mvn clean package -Dmaven.compiler.release=11 mvn clean package -Dmaven.compiler.release=17

重要限制

⚠️ 不能反向编译

Bash
# ❌ 错误:不能用 JDK 8 编译出 JDK 21 的字节码 mvn clean package -Dmaven.compiler.release=21 # 如果运行 JDK 是 8,会失败 # ✓ 正确:只能用高版本 JDK 编译低版本目标 mvn clean package -Dmaven.compiler.release=8 # JDK 21 可以编译 JDK 8

⚠️ API 兼容性

即使字节码版本正确,也不能使用高版本 API:

Java
// ❌ 即使配置了 --release 8,这行代码也会编译失败 List<String> list = List.of("a", "b"); // JDK 9+ API // ✓ 必须使用 JDK 8 的 API List<String> list = Arrays.asList("a", "b");

总结

  • Maven 运行的 JDK 版本编译输出的目标 JDK 版本 是两个独立的概念
  • 通过配置 maven.compiler.source/targetmaven.compiler.release,可以让高版本 JDK 编译出低版本兼容的字节码
  • 运行时 JDK 版本必须 >= 目标 JDK 版本(只能向下兼容,不能向上)
  • 即使字节码版本正确,也要注意不能使用高版本的 API

这种灵活性让开发者可以在开发环境使用最新的 JDK 特性(如更好的性能、工具支持),同时保持对生产环境低版本 JDK 的兼容性。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Odboy

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC 4.0 BY-SA 许可协议。转载请注明出处!