简介:jdk-17.0.3.1(jdk-17_windows-x64_bin.zip)是面向 Windows x64 平台的 Java SE 长期支持版本安装包,适合 Java 开发者、后端工程师及计算机专业学生搭建本地开发与运行环境。作为 Oracle 免费条款下可商用、可再分发的 JDK 发行版,它既能满足日常编码编译,也可用于生产部署与教学实验。压缩包共 416 个文件,约 171.62MB,包含 86 个 dll 动态库、35 个 exe 可执行程序、71 个 jmod 模块文件,以及 license、copyright、properties、policy 等许可与配置文档,另有 cacerts 证书库、jvm.cfg 虚拟机配置和 release 版本说明,结构完整、开箱即用。目前已有 1396 人学习下载,覆盖从环境搭建到模块化开发的常见需求,便于读者快速获得官方原版工具链,省去自行拼装组件的麻烦。
1. 为什么我硬盘里永远留着一份 jdk-17.0.3.1 的 windows-x64 bin.zip
上周帮同事排查一个 Spring Boot 3 项目启动就崩的问题,日志里一行UnsupportedClassVersionError: class file version 61.0,他本地装的是 JDK 8。换版本、清缓存、重配JAVA_HOME,折腾半小时才跑起来。这种事我遇到过太多次,所以自己机器上一直留着一份jdk-17_windows-x64_bin.zip,版本号锁死在 17.0.3.1。JDK 17 是 Java SE 的长期支持版本,按 Oracle 免费条款和条件许可,这个二进制包在生产环境可以免费使用、也能免费再分发,对中小团队来说省掉了授权层面的顾虑。这份 zip 是 Windows x64 平台的完整 JDK,解压即用,不需要安装器写注册表,特别适合需要多版本共存、或者 CI 机器上要临时切版本的场景。如果你也在维护 Java 项目、经常被版本问题绊住,这份东西值得放在手边。
2. 解压前先看清目录:jdk-17.0.3.1 里到底装了什么
很多人拿到 zip 直接双击解压,然后bin目录一加就完事,其实这个包里的结构值得先扫一遍。理解每个目录干什么,后面配环境变量、排查类加载问题会省很多事。
2.1 bin、conf、lib、jmods 四个核心目录的分工
解压jdk-17_windows-x64_bin.zip之后,根目录下会看到bin、conf、include、jmods、legal、lib这几个文件夹。bin里是可执行文件,java.exe、javac.exe、jar.exe、jlink.exe都在这里,日常敲命令靠它。conf是 JDK 17 才独立出来的配置目录,net.properties、security子目录、jaxp.properties都在这,以前这些散在lib里,现在拆开更清晰。lib放的是运行时依赖,modules文件(那个几百 MB 的大文件)就在这,JVM 启动时按需加载。jmods是模块化之后的.jmod文件,做自定义运行时镜像(jlink)时会用到。include是给 JNI 开发用的 C 头文件,纯 Java 项目可以忽略。
这里有个容易忽略的点:conf/security/java.security这个文件控制着安全提供者和算法策略,后面讲 TLS 握手失败时会回到它。lib/modules不要手动去改,它是打包好的模块镜像,改了 JVM 直接起不来。
2.2 版本号里的 0.3.1 意味着什么
17.0.3.1拆开看:17是大版本,0是次要版本,3是安全补丁号,最后那个.1是补丁的补丁。Java 的版本节奏是每季度发一次补丁,.3对应的是某个季度的安全更新,.1是在此基础上的紧急修复。选 17.0.3.1 而不是 17.0.3,通常是因为前者修了某个具体缺陷。生产环境锁版本时,我一般会记下完整四段号,而不是只写 17,因为不同补丁之间行为可能有细微差别,尤其是涉及 TLS、时区、字符集的修复。
2.3 和 JRE 的关系:为什么现在只发 JDK
从 JDK 9 开始,Oracle 就不再单独发 JRE 了,JDK 里已经包含完整的运行时。JDK 17 同样如此,bin目录里既有编译工具也有运行工具。如果你只需要跑 Java 程序,用jlink裁一个精简运行时出来就行,没必要再去找独立的 JRE 包。这也是为什么现在下载页面上只有 JDK 的 zip,没有单独的 JRE zip。
3. 从 zip 到能跑:Windows 下的解压、环境变量与验证
这一章是实打实的操作。我按自己在新机器上配环境的顺序写,每一步都有对应的命令和验证方式,照着走一遍基本不会翻车。
3.1 解压位置的选择与目录命名
不要解压到C:\Program Files下面,那个路径有空格,某些老构建脚本处理不好带空格的JAVA_HOME。我一般放在D:\dev\jdk\下面,目录名带上完整版本号,方便多版本共存:
# 假设 zip 下载在 D:\downloads\ # 用 PowerShell 解压(Windows 10 以上自带 Expand-Archive) Expand-Archive -Path "D:\downloads\jdk-17_windows-x64_bin.zip" -DestinationPath "D:\dev\jdk\" # 解压后会得到一个 jdk-17.0.3.1 目录 # 确认目录结构 dir D:\dev\jdk\jdk-17.0.3.1Expand-Archive是 PowerShell 内置命令,不用装 7-Zip 也能解。解压完确认一下bin\java.exe存在,如果解压出来是嵌套两层同名目录,手动把内层挪出来,保持D:\dev\jdk\jdk-17.0.3.1\bin\java.exe这个层级。
3.2 JAVA_HOME 与 Path 的正确配法
环境变量配错是新手最常见的翻车点。JAVA_HOME指向 JDK 根目录,不是bin;Path里加的是%JAVA_HOME%\bin,不是直接写死路径。这样以后换版本只改JAVA_HOME一处。
# 用 setx 设置用户级环境变量(需要重开终端生效) setx JAVA_HOME "D:\dev\jdk\jdk-17.0.3.1" # 把 %JAVA_HOME%\bin 追加到 Path # 注意:setx 会覆盖原有 Path,先读出来再拼,避免误删 for /f "tokens=2*" %a in ('reg query HKCU\Environment /v Path 2^>nul') do set "OLDPATH=%b" setx Path "%OLDPATH%;%JAVA_HOME%\bin"setx写的是注册表里的用户环境变量,当前终端不会立即生效,要新开一个 cmd 或 PowerShell。for /f那行是从注册表读旧 Path,防止setx直接覆盖导致原有路径丢失——这个坑我踩过,Path 被清空后一堆命令找不到。如果你不放心命令行,直接去「系统属性 → 环境变量」图形界面改也行,效果一样。
3.3 验证安装:java -version 与 javac -version 都要看
配完环境变量,新开终端跑两条命令:
java -version # 期望输出: # java version "17.0.3.1" 2022-04-22 LTS # Java(TM) SE Runtime Environment (build 17.0.3.1+2-LTS-6) # Java HotSpot(TM) 64-Bit Server VM (build 17.0.3.1+2-LTS-6, mixed mode, sharing) javac -version # 期望输出:javac 17.0.3.1两条都要看。只跑java -version有可能命中的是系统里另一个旧版本,javac -version能确认编译器也是 17。如果java能跑但javac报「不是内部或外部命令」,说明Path没配对,回去检查%JAVA_HOME%\bin是否真的加进去了。输出里的64-Bit确认你用的是 x64 版本,和下载的windows-x64_bin.zip对得上。
3.4 写一个最小程序验证编译与运行链路
光看版本号还不够,跑一个带新特性的小程序,确认编译和运行都正常:
// HelloRecord.java // 用 record 类型验证 JDK 17 的编译与运行 public class HelloRecord { record Point(int x, int y) {} public static void main(String[] args) { Point p = new Point(3, 4); // 文本块是 JDK 15 引入的特性,17 已正式支持 String msg = """ Point: %s Distance squared: %d """.formatted(p, p.x() * p.x() + p.y() * p.y()); System.out.print(msg); } }javac HelloRecord.java java HelloRecord # 期望输出: # Point: Point[x=3, y=4] # Distance squared: 25record是 JDK 16 转正、17 稳定支持的语法,文本块在 15 转正。这段代码同时用到了两个 17 的特性,能跑通说明编译器和运行时都是 17 级别。如果javac报「record 是预览特性」,说明你实际用的不是 17,回去查javac -version。
4. 多版本共存与构建工具对接:别让 JAVA_HOME 打架
实际开发中很少只装一个 JDK。老项目要 8,新项目要 17,机器上同时存在两三个版本是常态。这一章讲怎么让它们和平共处,以及 Maven、Gradle 怎么正确识别。
4.1 用目录命名区分版本,靠切换 JAVA_HOME 换版本
我的做法是D:\dev\jdk\下面放多个目录:jdk-8u301、jdk-11.0.12、jdk-17.0.3.1。切换版本时只改JAVA_HOME指向哪个目录,Path里始终是%JAVA_HOME%\bin。这样不用反复改 Path,也不会出现两个版本的java.exe同时出现在 Path 里导致命中顺序混乱。
# 切到 17 setx JAVA_HOME "D:\dev\jdk\jdk-17.0.3.1" # 切到 8 setx JAVA_HOME "D:\dev\jdk\jdk-8u301"每次改完要新开终端。如果嫌麻烦,可以写两个.bat脚本,双击就切,本质还是改JAVA_HOME。注意setx有长度限制(1024 字符),Path 特别长的时候要留意。
4.2 Maven 的 toolchains 与 maven.compiler.release
Maven 默认用JAVA_HOME指向的 JDK 编译。如果项目要求用 17 编译但你的JAVA_HOME是 8,可以在pom.xml里指定:
<properties> <!-- 指定编译目标版本,release 比 source/target 更严格 --> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>maven.compiler.release是 JDK 9 之后推荐的写法,它会同时约束source、target和bootclasspath,避免用高版本 JDK 编译出低版本字节码时误引用新 API。如果团队里 JDK 版本不统一,更规范的做法是配toolchains.xml,让 Maven 按项目要求自动选 JDK,而不是依赖JAVA_HOME。toolchains.xml放在~/.m2/下,里面声明每个 JDK 的路径和版本,pom.xml里用maven-toolchains-plugin绑定。
4.3 Gradle 的 java.toolchain 配置
Gradle 6.7 之后引入了 toolchain 概念,比 Maven 的 toolchains 更顺手:
// build.gradle java { toolchain { // 声明用 JDK 17 编译和运行,Gradle 会自动找匹配的 JDK languageVersion = JavaLanguageVersion.of(17) } }配了 toolchain 之后,Gradle 会自己去常见安装路径找 JDK 17,找不到才报错。它不会受JAVA_HOME影响,这对 CI 环境特别友好——CI 机器上JAVA_HOME可能是任意版本,但项目声明了 17,Gradle 就会用 17。前提是机器上确实装了 17,或者开了自动下载(org.gradle.java.installations.auto-download=true)。
4.4 IDE 里的 SDK 配置别漏掉
命令行配好了,IDE 里还要单独设。IntelliJ IDEA 里File → Project Structure → SDKs添加D:\dev\jdk\jdk-17.0.3.1,然后在Project里把 language level 设成 17。Eclipse 在Preferences → Java → Installed JREs里加。IDE 不读系统JAVA_HOME,它有自己的配置,这一步漏了会出现「命令行能编译、IDE 里报错」的诡异现象。
5. 避坑与排查:jdk-17.0.3.1 在 Windows 上的五个真实翻车记录
这一章全是我自己或身边人踩过的坑,按「现象 → 原因 → 解决」写。遇到问题先对照这里,能省不少搜索时间。
5.1 现象:java -version 显示 1.8,明明 JAVA_HOME 已经改成 17
原因:Path 里存在多个 java.exe,且旧版本的路径排在前面。Windows 按 Path 顺序查找,先命中的先执行。常见于以前装过 JDK 8 且安装器往C:\ProgramData\Oracle\Java\javapath写了软链接。
解决:把%JAVA_HOME%\bin移到 Path 最前面,或者删掉C:\ProgramData\Oracle\Java\javapath和C:\Windows\System32下的java.exe(后者是安装器放的,删之前确认没有其他程序依赖)。改完新开终端再验证。
5.2 现象:解压后运行 java 报「无法定位程序输入点 xxx 于动态链接库」
原因:zip 解压不完整,或者被杀毒软件拦截了部分文件。bin目录下的 DLL 和lib里的文件缺一不可。
解决:重新解压,解压前暂时关闭实时防护,解压后核对bin目录下java.exe、javaw.exe、server\jvm.dll是否都在。用Expand-Archive解压比某些第三方工具更可靠,不会漏文件。
5.3 现象:编译时报「错误: 不支持发行版本 17」
原因:javac实际用的不是 17,或者构建工具里source/target配成了别的版本。Maven 项目里如果pom.xml没配maven.compiler.release,会默认用插件的旧默认值。
解决:先javac -version确认编译器版本。Maven 项目检查pom.xml的maven-compiler-plugin配置,显式写<release>17</release>。Gradle 项目检查sourceCompatibility和targetCompatibility,或者直接用 toolchain。
5.4 现象:程序运行时报「no fontconfig.bfc found」或字体相关警告
原因:JDK 在 Windows 上依赖系统字体,lib目录下的fontconfig.bfc是字体配置缓存。如果这个文件损坏或缺失,AWT/Swing 程序会报字体警告,严重时界面渲染异常。
解决:从原始 zip 里重新提取lib\fontconfig.bfc覆盖过去。不要手动编辑这个文件,它是二进制格式。如果只是警告不影响功能,可以忽略;如果界面出问题,换回完整解压的版本。
5.5 现象:TLS 握手失败,报「unable to find valid certification path」
原因:cacerts信任库里的根证书过期或缺失,或者conf/security/java.security里的安全策略被改过。JDK 17 的cacerts默认密码是changeit,但一般不需要动它。
解决:先确认lib\security\cacerts文件存在且大小正常(约 100 多 KB)。如果怀疑证书问题,用keytool -list -cacerts查看信任库内容。企业内网自签证书的场景,需要用keytool -importcert把企业根证书导进去,而不是去改java.security关掉校验——关校验是饮鸩止渴,生产环境绝对不能这么干。
6. 用 jlink 裁一个 40MB 的运行时:把 jdk-17.0.3.1 用出花
JDK 17 的模块化让裁剪运行时变得很实际。一个完整 JDK 解压后 300MB 左右,但如果你只是部署一个 Spring Boot 服务,用jlink裁出来的运行时可以压到 40~60MB,镜像体积直接降一个量级。这一章讲怎么裁、怎么验证、以及我踩过的模块缺失坑。
先确认你的应用依赖哪些模块。最省事的办法是用jdeps分析:
# 分析一个 jar 的模块依赖 jdeps --multi-release 17 --ignore-missing-deps --print-module-deps your-app.jar # 输出类似:java.base,java.logging,java.sql,java.naming--print-module-deps会列出运行这个 jar 所需的最小模块集。--ignore-missing-deps是防止某些可选依赖缺失导致分析中断。拿到模块列表后,用jlink生成运行时:
# 生成自定义运行时镜像到 D:\dev\jre-mini jlink ^ --add-modules java.base,java.logging,java.sql,java.naming ^ --output D:\dev\jre-mini ^ --strip-debug ^ --no-header-files ^ --no-man-pages ^ --compress=2参数逐个说:--add-modules是要包含的模块,--output是输出目录,--strip-debug去掉调试信息,--no-header-files去掉 JNI 头文件,--no-man-pages去掉手册页,--compress=2是压缩级别(0 不压缩,1 常量字符串共享,2 更激进)。裁完的目录里同样有bin\java.exe,可以直接当 JRE 用。
验证裁出来的运行时能不能跑你的应用:
D:\dev\jre-mini\bin\java -jar your-app.jar如果报NoClassDefFoundError或module not found,说明模块漏了。常见漏网之鱼是java.desktop(用到 AWT/Swing 时)、jdk.crypto.ec(TLS 椭圆曲线加密)、java.xml(解析 XML 配置时)。补上对应模块重新jlink即可。我一般会先用完整 JDK 跑一遍应用,确认功能正常,再裁运行时,这样能把「应用本身的问题」和「模块缺失的问题」分开。
还有一个坑:jlink生成的运行时不能跨平台。你在 Windows 上裁出来的只能给 Windows 用,Linux 镜像要在 Linux 上裁,或者用--module-path指向对应平台的 jmods。Docker 多阶段构建里,常见做法是在构建阶段用完整 JDK 裁运行时,再把裁好的目录复制到最终镜像。
从那以后我每次部署新服务,都会先跑一遍jdeps确认模块列表,再jlink裁运行时,最后用裁出来的java跑一遍冒烟测试才敢上镜像。这套流程走顺了,镜像体积和启动速度都能明显改善。希望帮到你。
本文还有配套的精品资源,点击获取