java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java打包成exe

Java项目打包成exe的4大主流方法完整指南

作者:Java后端的Ai之路

本文为大家系统讲解就Java项目打包成exe的四大方案,包括官方jpackage、轻量Launch4j、商业exe4j和GraalVM原生编译,从环境准备到CI/CD集成,手把手教你生成专业安装包,让用户无需安装JDK也能开箱即用

作为一名Java开发者,你是否遇到过这样的场景:辛辛苦苦写了一个桌面工具,发给客户或者同事,对方双击JAR包毫无反应,或者弹出一个黑窗口报错"找不到主类",又或者被告知"你的电脑没有安装Java"。这种交付体验不仅让用户抓狂,也让开发者颜面尽失。

本文将系统性地讲解Java项目打包成exe的全部知识,从底层原理到实战操作,从工具选型到企业级CI/CD集成,让你看完就能直接上手,再也不用为Java程序的交付问题发愁。

一、痛点场景:为什么Java程序非要打成exe不可

1.1 真实场景还原

场景一:客户电脑没有JDK

你花了两周时间开发了一个数据报表生成工具,打包成JAR发给客户。客户双击后没有任何反应,打电话过来质问:"你这软件是不是坏了?我双击了什么都没有。"你远程一看,客户电脑上连Java都没装。你只能一步步教客户下载JDK、配置环境变量,折腾了一个小时,客户已经不耐烦了。

场景二:JDK版本不兼容

你的程序用Java 17开发,客户电脑上装的是Java 8。运行时直接抛出UnsupportedClassVersionError,客户看不懂报错信息,只觉得你的软件"不好用"。

场景三:JAR包双击没反应

即使客户装了正确版本的JDK,Windows系统下JAR文件的默认关联程序也可能不是Java。双击后系统问你"用什么程序打开",普通用户根本不知道该选什么。

场景四:命令行启动体验差

你告诉客户"打开cmd,输入java -jar xxx.jar",客户一脸茫然。即使照着做了,一个黑窗口一直挂在那里,关掉黑窗口程序就退出了,体验极其不专业。

场景五:企业客户要求安装包

企业级客户的IT部门有严格的软件分发规范,他们要求必须提供MSI或EXE安装包,能够静默安装、能在控制面板卸载、能创建桌面快捷方式。一个光秃秃的JAR包根本过不了企业IT的审核。

1.2 大白话总结

说白了,JAR包就像一个"半成品菜",你得自己有锅(JDK)、会开火(命令行)才能做熟。而EXE安装包就是"外卖成品",客户拿到手直接吃,什么都不用管。对于非技术用户来说,EXE才是他们认知中"正常的软件"。

二、解决方案总览:四大主流方案一览

目前业界将Java项目打包成exe主要有四大技术路线,各有适用场景:

方案技术本质代表工具适用场景
官方原生打包JRE裁剪+原生启动器+安装包jpackage(JDK 14+)企业级桌面应用,需要正规安装包
轻量包装器生成exe启动壳,调用外部JRELaunch4j、JSmooth小型工具,用户机器已有JRE
商业包装器功能丰富的exe生成工具exe4j、install4j商业软件,需要高级安装功能
AOT原生编译提前编译为机器码,无需JVMGraalVM Native ImageCLI工具、云原生、追求极致启动速度

接下来我们逐一深入讲解每个方案的原理和实操。

三、是什么:Java打包exe的底层原理

3.1 专业解释

Java程序本质上是运行在JVM(Java虚拟机)上的字节码,不能直接被 操作系统执行。将Java项目打包成exe,本质上是解决三个问题:

  1. 启动器问题:需要一个原生的Windows可执行文件(.exe)作为入口,它负责找到JVM并启动Java程序。
  2. 运行时问题:需要一个JRE(Java运行时环境)来执行字节码。可以依赖用户机器上已有的JRE,也可以自带一个裁剪后的JRE。
  3. 分发问题:需要将启动器、JRE、应用JAR包以及相关资源打包成一个用户友好的安装包。

3.2 大白话类比

把Java程序想象成一部电影。JAR包就是电影的数字胶片,JVM就是放映机,exe就是电影院。用户拿到JAR包,就像拿到一卷胶片,自己家里没有放映机(没装JDK)就看不了。而exe安装包就像把胶片、迷你放映机、播放说明一起打包成一个"家庭影院套装",用户买回来插上电就能看。

3.3 生活案例

你买了一个宜家的书架,包装盒里有木板(JAR包)、螺丝(依赖库)和说明书(MANIFEST.MF)。但是你需要自己有螺丝刀(JDK)才能组装。如果宜家把书架预装好,再配一个电动螺丝刀一起送货 上门,那就是exe安装包的体验——开箱即用。

四、为什么用:打成exe的核心价值

4.1 用户体验层面

4.2 技术管控层面

4.3 企业合规层面

五、演进史:Java打包技术是如何发展到今天的

5.1 Java 8及以前:javafxpackager时代

在Java 8时代,Oracle随JDK附带了一个叫做javafxpackager的工具(后来改名为javapackager),它可以将JavaFX应用打包成原生安装包。但这个工具有诸多限制:仅支持JavaFX应用、配置繁琐、生成的安装包体积大、跨平台支持差。大多数开发者还是依赖第三方工具如Launch4j、exe4j来完成打包。

5.2 Java 9:jlink与模块系统的革命

Java 9引入了JPMS(Java平台模块系统),同时提供了jlink工具。jlink可以根据应用实际使用的模块,裁剪出一个最小化的JRE运行时镜像。以前一个完整的JRE动辄200MB以上,使用jlink裁剪后可以压缩到40MB甚至更小。这为Java应用的轻量化分发奠定了基础。

5.3 Java 14:jpackage首次亮相(孵化阶段)

JEP 343提案引入了jpackage工具,作为Java 14的孵化特性。jpackage整合了jlink的裁剪能力和原生打包能力,可以直接生成Windows的exe/msi、macOS的dmg/pkg、Linux的deb/rpm安装包。这是Oracle官方首次提供一个功能完整的打包工具,但由于是孵化阶段,API和行为还可能变化,企业用户持观望态度。

5.4 Java 17:jpackage正式稳定

在Java 17这个LTS(长期支持)版本中,jpackage正式转正,成为JDK的标准工具。从此Java开发者有了一个官方、稳定、跨平台的打包解决方案。配合jlink的模块裁剪能力,jpackage成为企业级Java桌面应用打包的首选方案。

5.5 现代:GraalVM Native Image的崛起

近年来,GraalVM的Native Image技术逐渐成熟。它采用AOT(Ahead-Of-Time,提前编译)技术,在构建时就将Java字节码编译成原生机器码,生成的exe完全不需要JVM即可运行,启动时间从秒级降到毫秒级,内存占用也大幅降低。虽然在反射、动态代理等场景需要额外配置,但对于CLI工具、微服务等场景,Native Image正在成为新的趋势。

六、怎么用:三大方案实战教程

6.1 方案一:jpackage(官方推荐,企业首选)

jpackage是JDK 14+自带的官方打包工具,Java 17及以上版本稳定可用。它能够自动调用jlink裁剪JRE,并生成原生安装包。

6.1.1 环境准备

首先确认你的JDK版本:

java -version

输出应类似:

java version "17.0.9" LTS

在Windows上打包exe/msi还需要安装WiX Toolset 3.x(注意不是WiX 4),下载地址:https://wixtoolset.org/releases/

安装后将WiX的bin目录加入系统PATH,验证:

light.exe -help

6.1.2 第一步:构建可执行JAR包

使用Maven的maven-shade-plugin将所有依赖打入一个可执行JAR:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.example.myapp.Main</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

执行构建:

mvn clean package

构建完成后,在target/目录下会生成一个包含所有依赖的可执行JAR包,例如myapp-1.0.jar。

6.1.3 第二步:使用jpackage生成exe安装包

创建一个input目录,将JAR包放进去:

mkdir input
copy target\myapp-1.0.jar input\

执行jpackage命令:

jpackage ^
  --type exe ^
  --input input ^
  --dest output ^
  --name MyApp ^
  --main-jar myapp-1.0.jar ^
  --main-class com.example.myapp.Main ^
  --app-version 1.0.0 ^
  --vendor "羽师科技" ^
  --description "我的Java桌面应用" ^
  --icon app.ico ^
  --java-options "-Xmx512m" ^
  --win-dir-chooser ^
  --win-menu ^
  --win-shortcut

参数说明:

参数说明
--type exe生成exe安装包,也可以用msi
--input包含JAR包的输入目录
--dest输出目录
--name应用名称
--main-jar主JAR包文件名
--main-class主类全限定名
--app-version应用版本号
--icon应用图标(.ico格式)
--java-options传递给JVM的参数
--win-dir-chooser允许用户选择安装目录
--win-menu添加到开始菜单
--win-shortcut创建桌面快捷方式

执行完成后,在output/目录下会生成MyApp-1.0.0.exe安装包。用户双击即可安装,安装后桌面和开始菜单都会出现快捷方式。

6.1.4 第三步:生成免安装的应用镜像

如果你不想做安装包,只想生成一个解压即用的文件夹,可以使用--type app-image:

jpackage ^
  --type app-image ^
  --input input ^
  --dest output ^
  --name MyApp ^
  --main-jar myapp-1.0.jar ^
  --main-class com.example.myapp.Main ^
  --icon app.ico

这会在output/MyApp/目录下生成一个完整的应用文件夹,包含裁剪后的JRE和启动器,用户直接双击MyApp.exe即可运行,无需安装。

6.1.5 Maven插件集成(企业级用法)

在企业项目中,可以使用org.panteleyev:jpackage-maven-plugin将jpackage集成到Maven构建流程中:

<plugin>
    <groupId>org.panteleyev</groupId>
    <artifactId>jpackage-maven-plugin</artifactId>
    <version>1.6.0</version>
    <configuration>
        <name>MyApp</name>
        <appVersion>${project.version}</appVersion>
        <vendor>羽师科技</vendor>
        <destination>target/dist</destination>
        <input>target</input>
        <mainJar>${project.build.finalName}.jar</mainJar>
        <mainClass>com.example.myapp.Main</mainClass>
        <javaOptions>
            <option>-Xmx512m</option>
        </javaOptions>
        <type>EXE</type>
        <winMenu>true</winMenu>
        <winShortcut>true</winShortcut>
        <winDirChooser>true</winDirChooser>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>jpackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

这样执行mvn clean package就会自动完成编译、打JAR、生成exe的全过程。

6.2 方案二:Launch4j(轻量快速,适合小工具)

Launch4j是一个开源的轻量级Java exe包装器,它生成的exe本质上是一个启动器壳,负责查找JRE并启动JAR包。Launch4j本身不打包JRE,但可以指定旁置的JRE目录。

6.2.1 下载安装

从官网下载:https://launch4j.sourceforge.net/

解压后即可使用,提供GUI界面和命令行两种模式。

6.2.2 编写配置文件

创建launch4j-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<launch4jConfig>
  <dontWrapJar>false</dontWrapJar>
  <headerType>gui</headerType>
  <jar>target\myapp-1.0.jar</jar>
  <outfile>output\MyApp.exe</outfile>
  <errTitle>MyApp 启动错误</errTitle>
  <cmdLine></cmdLine>
  <chdir>.</chdir>
  <priority>normal</priority>
  <downloadUrl>https://www.java.com/download/</downloadUrl>
  <supportUrl></supportUrl>
  <stayAlive>false</stayAlive>
  <restartOnCrash>false</restartOnCrash>
  <manifest></manifest>
  <icon>app.ico</icon>
  <classPath>
    <mainClass>com.example.myapp.Main</mainClass>
  </classPath>
  <jre>
    <path>jre</path>
    <bundledJre64Bit>true</bundledJre64Bit>
    <bundledJreAsFallback>false</bundledJreAsFallback>
    <minVersion>17</minVersion>
    <maxVersion></maxVersion>
    <jdkPreference>preferJre</jdkPreference>
    <runtimeBits>64</runtimeBits>
  </jre>
  <heapSize>512</heapSize>
  <heapMaxSize>1024</heapMaxSize>
  <versionInfo>
    <fileVersion>1.0.0.0</fileVersion>
    <txtFileVersion>1.0.0</txtFileVersion>
    <fileDescription>我的Java桌面应用</fileDescription>
    <copyright>Copyright 2026 羽师科技</copyright>
    <productVersion>1.0.0.0</productVersion>
    <txtProductVersion>1.0.0</txtProductVersion>
    <productName>MyApp</productName>
    <companyName>羽师科技</companyName>
  </versionInfo>
</launch4jConfig>

关键配置说明:

6.2.3 执行打包

命令行方式:

launch4jc.exe launch4j-config.xml

或者打开Launch4j的GUI界面,导入配置文件,点击"Build wrapper"按钮。

6.2.4 自带JRE实现免安装运行

使用jlink裁剪一个最小JRE:

jlink ^
  --add-modules java.base,java.desktop,java.logging ^
  --output jre ^
  --strip-debug ^
  --compress=2 ^
  --no-header-files ^
  --no-man-pages

将生成的jre文件夹和MyApp.exe放在同一目录下,整个文件夹拷贝到任何Windows机器上都能直接运行,无需安装JDK。

6.3 方案三:GraalVM Native Image(极致性能,无需JVM)

GraalVM Native Image采用AOT编译技术,在构建时将Java字节码直接编译成原生机器码,生成的exe完全不依赖JVM,启动速度极快,内存占用极低。

6.3.1 环境准备

下载并安装GraalVM(建议GraalVM for JDK 17或21):https://www.graalvm.org/downloads/

设置JAVA_HOME指向GraalVM安装目录,并将bin加入PATH。验证:

java -version
native-image --version

在Windows上还需要安装Visual Studio Build Tools,包含Windows SDK和MSVC编译器。安装时勾选"使用C++的桌面开发"工作负载。

6.3.2 简单示例:Hello World

编写HelloWorld.java:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Native Image!");
    }
}

编译并生成原生exe:

javac HelloWorld.java
native-image HelloWorld

这会生成HelloWorld.exe,直接运行:

HelloWorld.exe

输出:

Hello, Native Image!

启动时间通常在10毫秒以内,内存占用只有几MB。

6.3.3 从JAR包生成原生exe

对于已有JAR包的项目:

native-image -jar target/myapp-1.0.jar MyApp

6.3.4 Maven插件集成

在pom.xml中添加GraalVM原生镜像插件:

<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.graalvm.buildtools</groupId>
                    <artifactId>native-maven-plugin</artifactId>
                    <version>0.10.1</version>
                    <extensions>true</extensions>
                    <configuration>
                        <mainClass>com.example.myapp.Main</mainClass>
                        <buildArgs>
                            <buildArg>--no-fallback</buildArg>
                            <buildArg>--initialize-at-build-time</buildArg>
                        </buildArgs>
                    </configuration>
                    <executions>
                        <execution>
                            <id>build-native</id>
                            <phase>package</phase>
                            <goals>
                                <goal>compile-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

执行原生编译:

mvn clean package -Pnative

6.3.5 反射配置(重要)

Native Image在编译时会做静态可达性分析,无法自动识别反射调用。如果你的代码使用了反射、动态代理、JNI等特性,需要提供配置文件。

在src/main/resources/META-INF/native-image/目录下创建reflect-config.json:

[
  {
    "name": "com.example.myapp.model.User",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  }
]

也可以使用Java agent自动收集配置:

java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image -jar target/myapp-1.0.jar

运行程序覆盖所有功能路径后,agent会自动生成所需的配置文件。

七、竞品对比:四大方案优劣势深度对比

对比维度jpackageLaunch4jexe4jGraalVM Native Image
是否官方是(JDK 14+自带)否(开源第三方)否(商业软件)否(Oracle GraalVM)
技术本质JRE裁剪+原生启动器+安装包轻量exe启动壳功能丰富的exe启动壳AOT编译为原生机器码
是否需要JVM是(自带裁剪JRE)是(依赖外部或旁置JRE)是(依赖外部或旁置JRE)否(完全原生)
启动速度正常(JVM启动,约1-3秒)正常(JVM启动)正常(JVM启动)极快(毫秒级)
内存占用标准JVM(约100-300MB)标准JVM标准JVM极低(约10-50MB)
安装包体积中等(约40-80MB,含裁剪JRE)极小(exe仅几十KB,不含JRE)小(exe约1-2MB,不含JRE)中等(约20-60MB)
反射/动态代理支持完全支持完全支持完全支持需要额外配置
跨平台打包否(必须在目标OS上构建)是(可在Linux/macOS生成Windows exe)否(仅Windows)否(必须在目标OS上构建)
生成安装包支持(exe/msi/dmg/deb/rpm)不支持(仅生成exe)不支持(仅生成exe,需配合install4j)不支持(仅生成exe)
学习曲线中等简单简单(GUI向导)较陡
商业授权免费(GPLv2 + CE)免费(MIT/BSD)收费(约$199起)免费(社区版)/企业版收费
最佳适用场景企业级桌面应用、JavaFX/Swing小型工具、用户已有JRE商业软件、需要高级功能CLI工具、微服务、云原生

7.1 选型建议

八、企业项目实战:CI/CD自动化打包流水线

在企业项目中,手动打包exe是不可接受的。需要将打包流程集成到CI/CD流水线中,实现代码提交后自动构建、自动测试、自动打包、自动发布。

8.1 GitLab CI配置示例

stages:
  - build
  - test
  - package
  - release
variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
# 构建JAR包
build_jar:
  stage: build
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn clean package -DskipTests
  artifacts:
    paths:
      - target/*.jar
    expire_in: 1 week
# 运行单元测试
run_tests:
  stage: test
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn test
  artifacts:
    reports:
      junit: target/surefire-reports/*.xml
# Windows平台打包exe
package_windows_exe:
  stage: package
  tags:
    - windows
  script:
    - choco install wixtoolset -y
    - mkdir input
    - copy target\myapp-1.0.jar input\
    - jpackage --type exe --input input --dest dist --name MyApp --main-jar myapp-1.0.jar --main-class com.example.myapp.Main --app-version %CI_COMMIT_TAG% --vendor "羽师科技" --icon app.ico --win-dir-chooser --win-menu --win-shortcut
  artifacts:
    paths:
      - dist/*.exe
    expire_in: 1 month
  only:
    - tags
# macOS平台打包dmg
package_macos_dmg:
  stage: package
  tags:
    - macos
  script:
    - mkdir input
    - cp target/myapp-1.0.jar input/
    - jpackage --type dmg --input input --dest dist --name MyApp --main-jar myapp-1.0.jar --main-class com.example.myapp.Main --app-version $CI_COMMIT_TAG --vendor "羽师科技" --icon app.icns
  artifacts:
    paths:
      - dist/*.dmg
    expire_in: 1 month
  only:
    - tags
# 发布到制品库
release:
  stage: release
  image: curlimages/curl:latest
  script:
    - curl -u $NEXUS_USER:$NEXUS_PASS --upload-file dist/MyApp-$CI_COMMIT_TAG.exe https://nexus.example.com/repository/releases/com/example/myapp/$CI_COMMIT_TAG/MyApp-$CI_COMMIT_TAG.exe
  only:
    - tags

8.2 Jenkins Pipeline配置示例

pipeline {
    agent any
    tools {
        jdk 'jdk-17'
        maven 'maven-3.9'
    }
    environment {
        APP_NAME = 'MyApp'
        APP_VERSION = "${env.BUILD_NUMBER}"
    }
    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', url: 'https://git.example.com/myapp.git'
            }
        }
        stage('Build & Test') {
            steps {
                bat 'mvn clean package'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        stage('Package EXE') {
            agent { label 'windows' }
            steps {
                bat '''
                    mkdir input
                    copy target\\myapp-1.0.jar input\\
                    jpackage --type exe ^
                        --input input ^
                        --dest dist ^
                        --name %APP_NAME% ^
                        --main-jar myapp-1.0.jar ^
                        --main-class com.example.myapp.Main ^
                        --app-version %APP_VERSION% ^
                        --vendor "羽师科技" ^
                        --icon app.ico ^
                        --win-dir-chooser ^
                        --win-menu ^
                        --win-shortcut
                '''
            }
            post {
                success {
                    archiveArtifacts artifacts: 'dist/*.exe', fingerprint: true
                }
            }
        }
        stage('Deploy') {
            steps {
                echo '部署到下载服务器...'
            }
        }
    }
}

8.3 企业级最佳实践

  1. 多平台矩阵构建:使用不同操作系统的Runner,同时构建Windows exe、macOS dmg、Linux deb。
  2. 版本号统一管理:从Git Tag或CI构建号获取版本号,确保安装包版本和代码版本一致。
  3. 代码签名:在打包后对exe进行数字签名,消除Windows SmartScreen警告。
  4. 自动更新集成:配合update4j或Sparkle等框架,实现软件自动更新。
  5. 制品归档:将每个版本的安装包归档到Nexus或Artifactory,支持历史版本回溯。
  6. 灰度发布:先发布给内部测试用户,验证无误后再全量发布。

九、常用场景与实战技巧

9.1 场景一:Swing桌面应用打包

Swing应用打包时需要注意中文乱码问题,在jpackage的--java-options中添加文件编码参数:

jpackage ^
  --type exe ^
  --input input ^
  --dest output ^
  --name MySwingApp ^
  --main-jar myapp.jar ^
  --main-class com.example.Main ^
  --java-options "-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xmx512m"

9.2 场景二:JavaFX应用打包

JavaFX从JDK 11开始被移除,需要单独引入依赖。使用jpackage时需要在--java-options中指定JavaFX模块路径:

jpackage ^
  --type exe ^
  --input input ^
  --dest output ^
  --name MyJavaFXApp ^
  --main-jar myapp.jar ^
  --main-class com.example.Main ^
  --java-options "--module-path lib --add-modules javafx.controls,javafx.fxml -Xmx512m"

也可以使用javafx-maven-plugin的jlink目标先生成包含JavaFX模块的运行时镜像,再用jpackage打包。

9.3 场景三:Spring Boot命令行工具打包

Spring Boot应用可以使用GraalVM Native Image打包成原生exe,实现毫秒启动:

<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
</plugin>

执行:

mvn -Pnative native:compile

生成的exe在target/目录下,直接运行即可,无需JVM。

9.4 场景四:带外部配置文件的应用

如果应用需要读取外部配置文件(如config.properties),在jpackage中使用--input目录将配置文件一起打包:

mkdir input
copy target\myapp.jar input\
copy config.properties input\
jpackage --type app-image --input input --dest output --name MyApp --main-jar myapp.jar

安装后配置文件会位于应用目录下,用户可以直接编辑。

9.5 常用技巧

技巧一:减小安装包体积

使用jlink的--compress=2和--strip-debug选项,可以将JRE体积进一步压缩:

jlink --add-modules java.base,java.desktop --output jre --strip-debug --compress=2 --no-header-files --no-man-pages

技巧二:自定义安装界面

jpackage支持通过--resource-dir自定义安装界面资源,包括欢迎页、许可协议等:

jpackage --type exe --input input --dest output --name MyApp --main-jar myapp.jar --resource-dir custom-resources

技巧三:设置JVM堆内存

在jpackage中通过--java-options预设JVM参数:

jpackage --type exe --input input --dest output --name MyApp --main-jar myapp.jar --java-options "-Xms256m -Xmx1024m -XX:+UseG1GC"

十、面试官高频面试题

面试题一:Java项目打包成exe有哪些方案?各自的优缺点是什么?

参考答案:

主要有四大方案:

面试题二:jpackage和jlink有什么区别和联系?

参考答案:

简单说,jlink是jpackage的底层依赖之一,jpackage = jlink + 原生启动器 + 安装包生成。

面试题三:为什么jpackage不能在Linux上打包Windows的exe?

参考答案:

jpackage生成的是平台原生的安装包,它依赖目标平台的原生工具链。在Windows上生成exe/msi需要调用WiX Toolset,在macOS上生成dmg需要调用系统的磁盘映像工具,在Linux上生成deb/rpm需要调用dpkg/rpm工具。这些工具都是平台相关的,无法跨平台调用。因此jpackage必须在目标操作系统上运行,不能交叉编译。如果需要多平台打包,需要使用多平台的CI/CD构建矩阵。

面试题四:GraalVM Native Image的工作原理是什么?有什么限制?

参考答案:

工作原理: Native Image采用AOT(Ahead-Of-Time)提前编译技术。在构建时,它通过静态分析扫描应用的字节码,找出所有可达的代码路径,然后将这些字节码直接编译成目标平台的原生机器码。同时它会在构建时初始化一部分类,将堆对象序列化到镜像中。最终生成的是一个完全独立的原生可执行文件,不需要JVM即可运行。

主要限制:

  1. 反射支持不完善:编译时无法预知动态加载的类,需要通过reflect-config.json手动配置。
  2. 动态代理需要配置:需要通过proxy-config.json声明动态代理接口。
  3. JNI需要配置:需要通过jni-config.json配置。
  4. 类加载器限制:不支持运行时创建自定义类加载器加载新类。
  5. 构建时间长:AOT编译比普通javac编译慢得多,大项目可能需要几十分钟。
  6. 峰值性能可能低于JIT:对于长时间运行的服务,JVM的JIT编译器经过运行时优化后峰值性能可能更高。

面试题五:如何减小Java应用打包后的体积?

参考答案:

  1. 使用jlink裁剪JRE:只包含应用实际需要的模块,而不是完整的JDK。一个最小的java.base模块JRE只有约20MB。
  2. 使用--strip-debug:移除调试信息,减小体积。
  3. 使用--compress=2:对JRE进行zip压缩。
  4. 使用--no-header-files和--no-man-pages:移除头文件和man手册。
  5. 排除未使用的依赖:在Maven/Gradle中排查并排除未使用的依赖库。
  6. 使用ProGuard混淆优化:对字节码进行优化和混淆,移除未使用的代码。
  7. 使用GraalVM Native Image:AOT编译后的原生二进制体积通常比JRE+JAR小。

面试题六:Launch4j生成的exe和jpackage生成的exe有什么本质区别?

参考答案:

Launch4j生成的exe只是一个轻量级的启动器壳(通常只有几十KB),它本身不包含JRE,运行时需要在系统中查找或在指定路径查找JRE,然后调用JVM来执行JAR包。JAR包可以被打包进exe内部,也可以放在外部。

jpackage生成的exe是一个完整的安装包,安装后会在目标机器上部署一个完整的应用目录,包含:原生启动器exe、裁剪后的JRE运行时、应用JAR包和资源文件。用户机器上完全不需要安装JDK。

简单说,Launch4j的exe是"借鸡生蛋"(用系统的JRE),jpackage的exe是"自带厨房"(自带裁剪JRE)。

总结

Java项目打包成exe,从早期的第三方工具群雄割据,到如今jpackage官方方案一统江湖,再到GraalVM Native Image开辟原生编译新赛道,技术生态已经非常成熟。选择哪种方案,取决于你的应用场景、用户群体和性能要求。对于大多数企业级桌面应用,jpackage是目前最稳妥、最规范的选择;对于追求极致性能的CLI工具,GraalVM Native Image值得投入研究。

以上就是Java项目打包成exe的4大主流方法完整指南的详细内容,更多关于Java打包成exe的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文