You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX打包后运行报错‘child process exited with code 1’求助

JavaFX打包exe后启动报错:child process exited with code 1(含UCanAccess数据库与线程)

IDE中运行包含UCanAccess数据库操作和线程逻辑的JavaFX应用一切正常,但用jpackage打包成Windows exe后启动,出现child process exited with code 1报错,无法运行。测试过无数据库和线程的简单JavaFX程序打包后可正常运行,问题大概率出在数据库或线程相关逻辑上。

相关代码与命令

控制器类代码

@Override
public void initialize(URL url, ResourceBundle rb) {
    Calendar calendar = new GregorianCalendar();
    Date toSql = calendar.getTime();
    lblCopy.setText("Copyright \u00a9"+df.format(toSql)+",All right reserved.");
    try {
     splashView();
     
    } catch (InterruptedException | SQLException | IOException ex) {
        Logger.getLogger(SplashController.class.getName()).log(Level.SEVERE, null, ex);
    }
}

//threads for the splash screen
public void splashView() throws InterruptedException, SQLException, IOException{
    //doTasks();
    
}  

//using the task class implementation
public void doTasks() throws InterruptedException, SQLException, IOException{
    
    createEnvironmentOnStart();
    verifyDatabase();
    
    TaskExecuter tasker = new TaskExecuter();
    Thread thread = new Thread(tasker);
    thread.isDaemon();
    thread.start();
    
    progBar.progressProperty().bind(tasker.progressProperty());
    progressLoading.textProperty().bind(tasker.messageProperty());
    
    
    tasker.setOnSucceeded((WorkerStateEvent event) -> {
        // create a new thread to run another task
        Thread thread1 = new Thread(() -> {
            //executeMain();
            Platform.exit();
        });
        thread1.start();
    });
}


// loading the main App dashboard.
public void executeMain(){
    Platform.runLater(() -> {
        try {
            Parent root =FXMLLoader.load(getClass().getResource("/SalesDataSheets/sds.fxml"));
            Scene scene = new Scene(root);
            Stage stage = new Stage();
            stage.setScene(scene);
            stage.setTitle("SalesDataSheets\u2122");
            stage.getIcons().add(new Image(SalesDataSheets.class.getResourceAsStream("images/sdslogo.png")));
            stage.show();
            splashPane.getScene().getWindow().hide();
        } catch (IOException ex) {
            Logger.getLogger(SplashController.class.getName()).log(Level.SEVERE, null, ex);
        }
    });
}

//checking for expiredate and out of stock goods.
public void verifyDatabase() throws SQLException{
    Calendar calendar = new GregorianCalendar();
    Date toSql = calendar.getTime();
    TreeMap<String,Date> expireNote = new TreeMap<>();
    TreeMap<String,Integer> outstokeNote = new TreeMap<>();
    msgToMsg = new ArrayList<>();        
    con = DriverManager.getConnection(newDB);
    state = con.createStatement();
    rs = state.executeQuery(sql2);
      while(rs.next()){
        String prod = rs.getString("product");
        int stoke = Integer.parseInt(rs.getString("stoke"));
        Date date = rs.getDate("Expire_Date");
        if(date != null){
            expireNote.put(prod, date);
        }else{
            expireNote.put(prod, null);
        }
        outstokeNote.put(prod, stoke);
      }
      
      Set<Map.Entry<String,Date>> me = expireNote.entrySet();
      Set<Map.Entry<String,Integer>> mu = outstokeNote.entrySet();
      
      for(Map.Entry<String,Date> element : me){
          String prod = element.getKey();
          Date date = element.getValue();
          
          long interval =(date.getTime()-toSql.getTime())/consta;
          
          if(interval > 30){
              System.out.println("The product "+prod+" is still at safe zone");
          }else if((interval>=0) && (interval<30)){
              System.out.println("The product " +prod+" is about "+interval+" days to expire");
              String nearlyExp = "The product " +prod+" is about "+interval+" days to expire";
              msgToMsg.add(nearlyExp);
          }else{
             System.out.println("The product " +prod+" has expired for about "+interval+" days now,please remove it from the DataBase");
             String Expired = "The product " +prod+" has expired for about "+(-1*interval)+" days now,please remove it from the DataBase";
             msgToMsg.add(Expired);
          }
          
          System.out.println(interval);
      }
      
      for(Map.Entry<String,Integer> element : mu){
          String prod = element.getKey();
          int stoke = element.getValue();
          if(stoke>50){
              System.out.println("the item is still available");
          }else if((stoke>0)&&(stoke<50)){
              System.out.println("The product "+prod+" is about to be out of stoke, for now only "+stoke+" items has remained");
              String nearlyOut = "The product "+prod+" is about to be out of stoke, for now only "+stoke+" items has remained";
              msgToMsg.add(nearlyOut);
          }else if(stoke ==0){
              System.out.println("The product "+prod+" is OUT OF STOKE");
              String Out = "The product "+prod+" is OUT OF STOKE";
              msgToMsg.add(Out);
          }
        }
    
      System.out.println(msgToMsg);
}

public void createEnvironmentOnStart() throws IOException, SQLException{
    //here all the files that are associated with the program resource and storage are created on first installation
    String dirPath = System.getProperty("user.home") +"\\Documents\\SalesDataSheets";
    String maker = "CREATE TABLE Table1 (ID AUTOINCREMENT PRIMARY KEY,product TEXT(255),purchase CURRENCY,sales CURRENCY,stoke INTEGER,ExpireDate DATETIME)";
    String maker2 = "CREATE TABLE Table3 (ID AUTOINCREMENT PRIMARY KEY,Product TEXT(255),Units INTEGER)";
    String maker3 = "CREATE TABLE Table4 (ID AUTOINCREMENT PRIMARY KEY,Expense TEXT(255),ExpenseAmount CURRENCY)";
    String maker4 = "CREATE TABLE Table5 (ID AUTOINCREMENT PRIMARY KEY,essuedAt DATETIME,totalSales CURRENCY,grossProfit CURRENCY,totalExpense CURRENCY,profloss CURRENCY)";
    Directory = new File(dirPath);
    dataBaseSub = new File(dirPath+"\\DataBase");
    filesSub = new File(dirPath+"\\SalesDatum");
    database = new File(dataBaseSub.getAbsolutePath()+"\\SalesDataDB.accdb");
    
    if(Directory.mkdirs()){
        boolean b1 = dataBaseSub.mkdirs();
        boolean b2 = filesSub.mkdirs();
        
        System.out.println("the file system created : "+b1+" : "+b2);
        
        try (Connection conn = DriverManager.getConnection(newDB+";newdatabaseversion=V2010")) {
           DatabaseMetaData dmd = conn.getMetaData();
          try (ResultSet rs1 = dmd.getTables(null, null, "Clients", new String[] { "TABLE" })) {
                 if (rs1.next()) {
                     System.out.println("Table [Clients] already exists.");
                    } else {
                         System.out.println("Table [Clients] does not exist.");
                          try (Statement s = conn.createStatement()) {
                                s.executeUpdate(maker);
                                System.out.println("table 1 create successively");
                                s.executeUpdate(maker2);
                                System.out.println("table 3 create successively");
                                s.executeUpdate(maker3);
                                System.out.println("table 4 create successively");
                                s.executeUpdate(maker4);
                                System.out.println("table 5 create successively");
                        }
                     }
                 }
        }
        
    }else{
        System.out.println("the file system is adequate");
        System.out.println(System.getProperty("user.home"));
    }
}

TaskExecuter类代码

public class TaskExecuter extends Task<Integer> {

    @Override
    protected Integer call() throws Exception {
        int state = 0;
        for(int i=0;i<=100;i++){
            
            state = state +i;
            updateValue(state);
            updateProgress(i,100);
            updateMessage("Loading..."+String.valueOf(i)+"%");
            Thread.sleep(100);
        }
        
        return state;
    }
}

jpackage命令

jpackage --type exe --input . --dest . --main-class SalesDataSheets.SalesDataSheets --main-jar .\SalesDataSheets1.jar --module-path "C:\Program Files\Java\javafx-jmods-18.0.1" --add-modules javafx.controls,javafx.fxml --icon "E:\Netbeans\SalesDataSheet\sdsIcon.ico" --win-shortcut

问题排查与解决方案

1. 线程逻辑错误

  • 问题点:代码中thread.isDaemon();只是获取守护线程状态,并未设置线程为守护线程,非守护线程可能在程序启动时导致资源占用异常。
  • 修复:将该行改为:
thread.setDaemon(true); // 设置为守护线程,避免阻止程序正常启动/退出
  • 额外优化:verifyDatabase和createEnvironmentOnStart是耗时的IO/数据库操作,当前在JavaFX初始化线程(UI线程)中直接调用,可能导致UI阻塞甚至启动失败。应将这些操作放到TaskExecuter的call方法中,在后台线程执行:
// 修改TaskExecuter的call方法
@Override
protected Integer call() throws Exception {
    int state = 0;
    // 先执行数据库初始化与校验
    createEnvironmentOnStart();
    verifyDatabase();
    // 再执行进度更新
    for(int i=0;i<=100;i++){
        state = state +i;
        updateValue(state);
        updateProgress(i,100);
        updateMessage("Loading..."+String.valueOf(i)+"%");
        Thread.sleep(100);
    }
    return state;
}

同时修改doTasks方法,移除直接调用这两个方法的代码,避免UI线程阻塞。

2. UCanAccess依赖缺失

  • 问题点:打包时未将UCanAccess及其依赖(如jackcess、commons-lang3、commons-logging等)包含到程序运行环境中,导致运行时找不到JDBC驱动。
  • 修复:
    • 确保主jar(SalesDataSheets1.jar)是包含所有依赖的fat jar(可通过Maven Shade或Gradle Shadow插件打包)。
    • 若未打包成fat jar,需将UCanAccess相关依赖jar放到jpackage命令的--input目录下,确保打包时被包含到runtime中。
    • 检查newDB变量的JDBC URL是否正确,尤其是数据库文件路径,避免使用相对路径导致打包后找不到数据库文件。

3. 权限与路径问题

  • 问题点:程序尝试在用户Documents目录下创建文件/目录,打包后的exe可能因权限不足无法访问该路径,或者路径拼接存在转义问题。
  • 修复:
    • 测试时可添加--win-console参数到jpackage命令,启动exe时弹出控制台,查看具体的异常日志:
jpackage --type exe --input . --dest . --main-class SalesDataSheets.SalesDataSheets --main-jar .\SalesDataSheets1.jar --module-path "C:\Program Files\Java\javafx-jmods-18.0.1" --add-modules javafx.controls,javafx.fxml --icon "E:\Netbeans\SalesDataSheet\sdsIcon.ico" --win-shortcut --win-console
  • 确认System.getProperty("user.home")返回的路径正确,可在控制台打印路径验证。
  • 若存在权限问题,可尝试右键exe选择"以管理员身份运行"测试,或修改程序目录到无需管理员权限的位置。

4. JavaFX线程规则遵守

  • 问题点:tasker.setOnSucceeded中启动的线程直接调用Platform.exit()可能存在线程安全问题,违反JavaFX线程规则。
  • 修复:将Platform.exit()放到Platform.runLater中执行:
tasker.setOnSucceeded((WorkerStateEvent event) -> {
    Platform.runLater(() -> {
        // executeMain();
        Platform.exit();
    });
});

内容的提问来源于stack exchange,提问作者Paul_m

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:44:56