2024年11月30日 星期六

How to interpret the caused by sections of a Java stack trace?

Java執行出錯丟出例外時,常會列印一串 Caused by 訊息,其格式為
   java.lang.Exception: Exception in xxx
       at ......... (....java: ..)
       .......
       at ......... (....java: ..)
   Caused by: java.lang.Exception: Exception in yyy
       at ......... (....java: ..)
       .......
       at ......... (....java: ..)
   Caused by: java.lang.Exception: Exception in zzz
       at ......... (....java: ..)
       .......
       at ......... (....java: ..)

這表示先有 zzz 錯誤,然後造成 yyy 錯誤,然後造成 xxx 錯誤。因此,最初錯誤原因為最後Caused by 指出的 zzz 錯誤。至於每個錯誤後面都會跟著很多 at,印出丟出例外當時的方法堆疊內容,越後面的 at 程式碼越早執行。


 public class CausedByExample {
    public static void main(String[] args) {
        try {
            method1();  // line 4
        } catch (Exception e) {
            // 此行指令表明 執行方法main出現例外 將列印丟出例外時的堆疊記錄內容
            e.printStackTrace();
        }
    }

    public static void method1() throws Exception {
        try {
            method2();  // line 13
        } catch (Exception e) {
            // 此行指令表明 執行方法1出現例外 是由 執行方法2的例外e造成,將列印
            // java.lang.Exception: Exception in method1
            //  逐層列印丟出方法1例外時的堆疊記錄內容
            throw new Exception("Exception in method1", e);  // line 18
            //  public Exception(String message, Throwable cause) 
            //  產生新例外,包含例外說明字串 message,及造成本例外的原因 cause
        }
    }

    public static void method2() throws Exception {
        // 此行指令表明 執行方法2出現例外,將列印
        // java.lang.Exception: Exception in method2
        //  逐層列印丟出方法2例外時的堆疊記錄內容
        throw new Exception("Exception in method2");  // line 26
    }
}

Output:

上面程式在method2產生例外,由method1接收,再包裝成原因產生新例外,由main接收,列印e.printStackTrace。其列印內容說明,Exception in method2 造成 Exception in method1

java.lang.Exception: Exception in method1
	at CausedByExample.method1(CausedByExample.java:18)
	at CausedByExample.main(CausedByExample.java:4)
Caused by: java.lang.Exception: Exception in method2
	at CausedByExample.method2(CausedByExample.java:26)
	at CausedByExample.method1(CausedByExample.java:13)
	... 1 more

2024年11月12日 星期二

how to set up NetBeans environment for JavaFX applications

JavaFX 為 Java 繼 Awt, Swing 之後推出的第 3 代圖形介面 (GUI) 套件,多了場景建立器 (Scene Builder),排版配置檔 CSS 等支援能力。 利用 NetBeans 整合開發環境 (IDE) 撰寫 JavaFX 應用時,常遇到開發環境如何建立的問題。很容易會遇到如下錯誤:

   Error occurred during initialization of boot layer
   java.lang.module.FindException: Module javafx.controls not found
或
   java.lang.module.FindException: Module javafx.fxml not found

以下整理幾點 NetBeans 整合 JavaFX SDK,SceneBuilder 場景建立器,詳查 Build 腳本的方法,供除錯參考。

✅ NetBeans 整合 JavaFX SDK 方法
     Project Properties
       Libraries/
           Java Platform: JDK xx (Default)
           Compile/Compile-time Libraries:
               Classpath: JavaFX yy
           Run/Run-time Libraries:
               Modulepath: JavaFX yy

       Run/
           Configuration: <default config>
           VM Options:
               --add-modules javafx.controls,javafx.fxml,javafx.media
         
✅ NetBeans 整合 SceneBuilder 方法
     Tools/Options/Java/JavaFX
        JavaFX Scene Builder Integration
          Scene Builder Home: C:\Users\zz\AppData\Local\SceneBuilder
    
✅ NetBeans除錯想看Build執行腳本內容
     Tools/Options/Java/Ant:
        Ant Home: ...
        [v] Always Show Output

     Verbosity Level: Quiet/Normal/[Verbose]/Debug

    即可觀看Ant Target (build.xml) Output

註1: 建立 JavaFX 開發環境所須安裝套件如下
1.OpenJDK
  https://learn.microsoft.com/zh-tw/java/openjdk/download
    microsoft-jdk-21.0.5-windows-x64.msi (不含JavaFX)
    microsoft-jdk-21.0.5-macos-aarch64.pkg
  https://www.azul.com/downloads/?package=jdk-fx#zulu
    zulu21.38.21-ca-fx-jdk21.0.5-win_x64.msi (含JavaFX)

2.NetBeans
  https://netbeans.apache.org/download/index.html
    Apache-NetBeans-22-bin-windows-x64.exe
    Apache-NetBeans-22.pkg

3.SceneBuilder
  https://gluonhq.com/products/scene-builder/
    SceneBuilder-23.0.1.msi
    SceneBuilder-23.0.1-aarch64.dmg

4.JavaFX SDK
  https://gluonhq.com/products/javafx/
    openjfx-17.0.13_windows-x64_bin-sdk.zip
    openjfx-17.0.13_osx-aarch64_bin-sdk.zip
    
    
註2: NetBeans 要看到 Scene Builder Home,須要安裝且啟動(Activate)如下任一插件
     Tools/Plugins:
       JavaFX 2
       或
       JavaFX Implementation for Windows

     安裝之後,針對專案 .fxml 按右鍵,才會看到如下選項
       Open 連動開啟場景建立器畫面
       Edit 開啟.fxml文字畫面
       Make Controller 產生 .fxml 中 fx:controller 屬性指定的控制器.java類別檔

註3: NetBeans 要能看到 JavaFX yy 類別庫,須設定
     Tools/Libraries/Libraries:
       Library Name: JavaFX yy
       Classpath/Add JAR/Folder...:
         ...\javafx-sdk-yy\lib\javafx-swt.jar    
         ...\javafx-sdk-yy\lib\javafx.base.jar    
         ...\javafx-sdk-yy\lib\javafx.controls.jar    
         ...\javafx-sdk-yy\lib\javafx.fxml.jar    
         ...\javafx-sdk-yy\lib\javafx.graphics.jar    
         ...\javafx-sdk-yy\lib\javafx.media.jar    
         ...\javafx-sdk-yy\lib\javafx.swing.jar    
         ...\javafx-sdk-yy\lib\javafx.web.jar    

註4: NetBeans 要能點選原始碼類別,按右鍵點選
       Show Javadoc (Alt-F1) 看到類別 註解說明,須設定
     Tools/Libraries/Libraries:
       Library Name: JavaFX yy
       Javadoc/Add URL...: https://docs.oracle.com/javafx/2/api/

註5: NetBeans 要能點選原始碼類別,按右鍵點選 
       Navigate> Go to Source (Ctrl-Shift-B) 看到類別 原始碼,須設定
     Tools/Libraries/Libraries:
       Library Name: JavaFX yy
       Sources/Add JAR/Folder...: ...\javafx-sdk-yy\src.zip    

2024年11月11日 星期一

controller class not found or fxml load error when running JavaFX applications

執行 JavaFX 圖形介面程式,使用視窗配置檔 .fxml 時,若出現 fxml載入錯誤控制器類別找不到 錯誤,很可能是因為 .fxml 配置檔的控制器指定值沒有寫對。可打開 .fxml 檔,檢查容器 (XXPane) 標籤的 fx:controller 控制器欄位值是否正確。若有使用套件包裝,其套件路徑是否正確。

如下 fxml 配置檔範例中,假設 DrawShapesController.java 控制器宣告歸屬套件 package com.abc;,則其值前面要加上 com.abc 套件路徑,在依據 fxml 配置檔載入控制器時,才找得到控制器類別。

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity" 
xmlns="http://javafx.com/javafx/8.0.60" 
xmlns:fx="http://javafx.com/fxml/1" 
fx:controller="com.abc.DrawShapesController">
...
</BorderPane>

註1: 常見 fxml載入錯誤 及 控制器類別找不到錯誤  例子。

Exception in Application start method
java.lang.reflect.InvocationTargetException
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  .....
  at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)

Caused by: java.lang.RuntimeException: Exception in Application start method
  at javafx.graphics@19/.....LauncherImpl.launchApplication1(LauncherImpl.java:901)
  at javafx.graphics@19/.....LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
  at java.base/java.lang.Thread.run(Thread.java:834)

Caused by: javafx.fxml.LoadException: ..../DrawShapes.fxml:8
  at javafx.fxml@19/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2707)
  ......
  at ......DrawShapes.start(DrawShapes.java:16)
  ......

Caused by: java.lang.ClassNotFoundException: DrawShapesController
  at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
  ......

Exception running application .....DrawShapes

註2: 有時也會看到如下 找不到類別定義錯誤 例子。

Caused by: java.lang.NoClassDefFoundError: DrawRandomLinesController (wrong name: DrawRandomLinesController)

2024年11月9日 星期六

how to write a JavaFX application without use of SceneBuilder?

一般 JavaFX 的視窗要利用 Gluon公司釋放的 SceneBuilder 工具,建立視窗配置檔 .fxml,再由程式載入套用。其實不利用視窗配置檔,也可建立視窗,只是配置元件位置較麻煩。以下參考Copilot輸出範例,展示簡單版,不載入配置檔,視窗建立法。


// JavaFX_noscenebuilder.java // 展示如何不用場景建立器,配置元件位置, // 直接用程式部署元件的 JavaFX 視窗程式寫法 // // 命令列編譯指令 // > javac -classpath "...\javafx-sdk-xx\lib\javafx-swt.jar;...;." \ // JavaFX_noscenebuilder.java // // 命令列執行指令 // > java --add-modules javafx.controls,javafx.fxml \ // -classpath . \ // --module-path "...\javafx-sdk-xx\lib\javafx-swt.jar;..." \ // JavaFX_noscenebuilder // // 引用 JavaFX 套件 import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; // JavaFX 視窗類別須繼承自 javafx.application.Application public class JavaFX_noscenebuilder extends Application { @Override // 舞台啟動方法,接收舞台參數 primaryStage,建立場景 public void start(Stage primaryStage) { primaryStage.setTitle("Hello World!"); // 設定舞台標題 Button btn = new Button(); // 建立按鈕 btn.setText("Say 'Hello World'"); // 設立按鈕顯示字串 // 設定按鈕事件處理器,列印歡迎字串 btn.setOnAction(event -> System.out.println("Hello World!")); StackPane root = new StackPane(); // 建立堆疊面板,當場景根節點 root.getChildren().add(btn); // 將按鈕加入堆疊面板的小孩容器 // 在場景根節點root下,建立300x100場景 Scene scene = new Scene(root, 300, 100); primaryStage.setScene(scene); // 設定舞台場景 primaryStage.show(); // 顯示舞台 } // 測試主程式 public static void main(String[] args) { // 載入JavaFX執行期環境,建立應用程式物件,呼叫 start()啟動舞台及場景 Application.launch(args); } }
    註: 程式編譯及執行所須參考的套件如下
  1. JavaFX xx -javafx-swt.jar
  2. JavaFX xx -javafx.base.jar
  3. JavaFX xx -javafx.controls.jar
  4. JavaFX xx -javafx.fxml.jar
  5. JavaFX xx -javafx.graphics.jar
  6. JavaFX xx -javafx.media.jar
  7. JavaFX xx -javafx.swing.jar
  8. JavaFX xx -javafx.web.jar
  9. JDK yy