---
title: "Java Swingとは？廃止されたのはアプレットAPIだけ｜使い方・JTable・jpackage配布"
url: "https://www.issoh.co.jp/column/details/2903/"
published: 2024-07-03
updated: 2026-10-23
categories: ["アプリ"]
publisher: "株式会社一創"
---

# Java Swingとは？廃止されたのはアプレットAPIだけ｜使い方・JTable・jpackage配布

Java Swing（パッケージ名は `javax.swing`）は、JDKに標準で入っているデスクトップGUIツールキットです。「Swingは廃止された」という話をよく見かけますが、2026年3月のJDK 26で削除されたのはアプレット関連のAPIで、Swing本体は削除されていません。2026年9月15日にGAになったJDK 27にも、2027年3月にGA予定のJDK 28の対象JEP（2026年10月22日時点）にも、Swingを対象とするものはありません。

この記事では、Swingの位置づけ、AWTとの違い、JDK 26で消えた範囲、最初のウィンドウと表（`JTable`）を出すコード、Swingで必ず守るスレッドの規則、Javaが入っていないPCへ配る方法、2026年にSwingを選ぶかどうかの判断基準を順に扱います。掲載したコードはすべて Temurin 25.0.4.1 と JDK 27（いずれもmacOS）でコンパイル・実行し、ボタン操作や表の並べ替えまで確認しました。

## まとめ

- Swingは `java.desktop` モジュールに含まれるGUIツールキットで、JDKがあれば追加のインストールなしで使える。JDKへの標準同梱はJ2SE 1.2（1998年12月）から。
- JDK 26のJEP 504で消えたのは `java.applet` パッケージ、`javax.swing.JApplet` などアプレット関連のAPI。AWT・Swing・Java 2Dは「引き続きサポートされ、機能強化される」とOpenJDK側が明言している。
- AWTはOSのネイティブ部品を使う重量コンポーネント、Swingは部品をJava側で描く軽量コンポーネント。ただしSwingもレイアウトやイベントはAWTの仕組みを使うため、実際のコードでは両方をimportする。
- Swingのコンポーネントはスレッドセーフではない。画面の生成・更新はイベントディスパッチスレッド（EDT）で行い、重い処理は `SwingWorker` で別スレッドへ逃がす。
- Javaが入っていないPCへ配るなら `jpackage` でランタイムごと同梱する。`--add-modules java.desktop` で絞ると、アプリイメージは138MBから82MBに縮んだ。
- 既存システムの保守や社内ツールには今も合理的な選択肢。モバイル・Web配布やアニメーション中心のUIが要件なら、JavaFXかWeb技術を検討する。

## Java Swingとは：JDKに標準で入っているGUIツールキット

### javax.swingパッケージとJFCの位置づけ

Swingは、Javaでウィンドウやボタン、表などのデスクトップ画面を作るためのクラス群です。Oracleの定義では、Swingは **JFC（Java Foundation Classes）** の一部で、JFCは「Swing GUI Components」「Pluggable Look-and-Feel Support」「Accessibility API」「Java 2D API」「Internationalization」の5要素で構成されます。

JFCがJDKに同梱されたのは1998年12月のJ2SE 1.2からで、このときSwingとJava 2D APIが標準に入りました。現在のJDKでは `java.desktop` モジュールに属しており、Oracle JDKでもOpenJDK系のTemurinでも、JDKを入れればそのまま `import javax.swing.*;` で使えます。検索で「javax.swing」と「java swing」が同じものとして扱われるのはこのためで、前者がパッケージ名、後者が通称です。

### よく使うSwingコンポーネント

Swingのクラスは、AWTの同種クラスと区別するために名前の先頭に「J」が付きます。最初に覚えるのは次の程度で足ります。

| クラス           | 役割         | AWTの近いクラス    |
| ------------- | ---------- | ------------ |
| `JFrame`      | アプリのウィンドウ  | `Frame`      |
| `JPanel`      | 部品をまとめる入れ物 | `Panel`      |
| `JLabel`      | 文字・画像の表示   | `Label`      |
| `JButton`     | ボタン        | `Button`     |
| `JTextField`  | 1行の入力欄     | `TextField`  |
| `JTable`      | 表          | なし           |
| `JTree`       | ツリー表示      | なし           |
| `JScrollPane` | スクロール領域    | `ScrollPane` |
| `JOptionPane` | 定型のダイアログ   | なし           |

`JTable` や `JTree` のように、AWTに対応するクラスが無い部品を標準で持っていることが、業務アプリでSwingが広く使われた理由の一つです。`JTable` の使い方は後の章でコード付きで扱います。

## SwingとAWTの違い

### AWTとは：OSのネイティブ部品を使う重量コンポーネント

AWT（Abstract Window Toolkit、パッケージは `java.awt`）は、Swingより前からあるJavaのGUIライブラリです。AWTのボタンはWindowsならWindowsの、macOSならmacOSのネイティブ部品として表示されます。Oracleの `java.awt.Component` のドキュメントでは、ネイティブウィンドウに結び付く部品を「heavyweight（重量）」、結び付かない部品を「lightweight（軽量）」と定義しています。

Swingの部品は原則として軽量で、ボタンや表の見た目をJava側で描きます。例外は `JFrame`・`JDialog`・`JWindow` などのトップレベルコンテナで、これらはOSのウィンドウを持つ重量コンポーネントです。JDK 25では、ウィンドウに配置した `JButton` の `isLightweight()` は true、`JFrame` は false を返しました。

### AWTとSwingの比較

| 項目      | AWT        | Swing            |
| ------- | ---------- | ---------------- |
| パッケージ   | `java.awt` | `javax.swing`    |
| 部品の描画   | OSのネイティブ部品 | Javaが描画（軽量）      |
| 見た目     | OSごとに異なる   | Look and Feelで切替 |
| 表・ツリー   | なし         | `JTable`・`JTree` |
| クラス名    | `Button`   | `JButton`        |
| JDKへの同梱 | JDK 1.0から  | J2SE 1.2から       |

見た目をOSに合わせたいだけならAWTでも動きますが、部品の種類とカスタマイズ性で差が大きいため、新しく画面を作るならSwingを使います。

### SwingのコードにAWTのimportが残る理由

「AWTとSwingのどちらを使うか」は二者択一ではありません。Swingは部品（`JButton` など）を置き換えただけで、配置を決める `BorderLayout`、イベントを表す `java.awt.event.ActionEvent`、描画に使う `Graphics` はAWTのクラスをそのまま使います。後述のサンプルでも `java.awt.BorderLayout` をimportしています。

避けるべきなのは、同じ画面に `Button` と `JButton` を混ぜることです。重量・軽量コンポーネントの混在はJDK 6 Update 12以降で対応されていますが、AWT部品にはSwingのLook and Feelが適用されないため、見た目がそろいません。部品はJ付きのクラスに統一し、AWTはレイアウト・イベント・描画の基盤として使う、と分けて考えると迷いません。

## Java Swingは廃止されたのか：JDK 26で削除された範囲

### JEP 504で削除されたAPI

2026年3月にリリースされたJDK 26では、JEP 504「Remove the Applet API」によりアプレット関連のAPIが削除されました。OpenJDKの[client-libs-devメーリングリスト](https://mail.openjdk.org/pipermail/client-libs-dev/2025-July/030640.html)で示された削除範囲は次のとおりです。

- `java.applet` パッケージ全体
- `javax.swing.JApplet` クラス
- `java.beans` のアプレット関連API（`AppletInitializer` など）

削除されたSwingのAPIは、`JApplet` クラスだけではありません。[JDK 25のAPIドキュメント](https://docs.oracle.com/en/java/javase/25/docs/api/java.desktop/javax/swing/RepaintManager.html)には `RepaintManager.addDirtyRegion(Applet, int, int, int, int)` が削除予定の非推奨として載っていますが、[JDK 26](https://docs.oracle.com/en/java/javase/26/docs/api/java.desktop/javax/swing/RepaintManager.html)では `JComponent` 版と `Window` 版の2つだけになっています。ブラウザーでアプレットを動かす仕組み自体はすでに主要ブラウザーから消えており、アプレットAPIもJDK 9で非推奨、JDK 17で削除予定になっていました。JDK 26はその最後の片付けです。

### JDK 26・27・28でのSwing本体の扱い

JEP 504の告知記事（[inside.java、2025年12月3日](https://inside.java/2025/12/03/applet-removal/)）は、残りの部分について「The rest of the java.desktop module (i.e. AWT, Swing, Java 2D) in the JDK is unaffected in functionality and continues to be supported and enhanced.」と明記しています。AWT・Swing・Java 2Dは機能面で影響を受けず、今後もサポートと機能強化が続く、という意味です。

2026年9月15日にGAとなったJDK 27のJEPは9件（G1の既定化、オブジェクトヘッダの圧縮の既定化、TLS 1.3の耐量子ハイブリッド鍵交換など）で、`java.desktop` やSwingを対象とするものは含まれていません。次のJDK 28も、[OpenJDKのプロジェクトページ](https://openjdk.org/projects/jdk/28/)（2026年10月22日更新）で対象になっている10件はValue Objects（プレビュー）や構造化並行性などで、Swingに関わるJEPはありません。JDK 26の変更全体は[Java 26（JDK 26）の新機能10個のJEP](/tech/details/11412/)、JDK 27は[Java 27の変更点と非LTSの運用](/tech/details/17712/)で整理しています。

### 「Swingは廃止」と誤解される背景

Swingの廃止と混同されやすい変更は2つあります。1つはアプレットの削除で、「`JApplet` が消えた」が「Swingが消えた」と受け取られがちです。アプレットAPIの削除をJDK 24と書いた解説も見かけますが、JDK 24では削除されておらず、削除はJDK 26です。もう1つはJavaFXで、Swingの後継として打ち出されたJavaFXは、JDK 11でOracle JDKから切り離され、現在はOpenJFXとして別配布になっています（最新は2026年9月15日公開のJavaFX 27）。

JDKから外れたのはむしろJavaFXの側で、SwingはJDK 27でも標準で入っています。JavaFXが廃止されたと言われる理由とSwingとの違いは、[JavaFXの廃止の真相とSwingとの違い](/column/details/2904/)で整理しています。

## Java Swingの使い方：ウィンドウの作成と実行

### Swingの実行準備とJDKの選択

SwingはJDKに含まれるため、ライブラリの追加やビルドツールは要りません。新しく入れるなら、LTSのJDK 25が扱いやすい版です。2026年10月20日のCritical Patch Update（CPU）で25.0.5が出ており、Oracle JDK 25のPremier Supportは[Oracle Java SEサポート・ロードマップ](https://www.oracle.com/jp/java/technologies/java-se-support-roadmap.html)で2030年9月までとされています。これはOracleのサポート契約の話で、Temurinなど他の配布元の保守期間は各配布元の方針で決まります。JDKの種類やライセンスの違いは[Java SEとJDK・JRE・JVMの違い](/column/details/3113/)を参照してください。

ファイルを1つ書いたら、ターミナルで次のどちらかを実行します。JDK 11以降は、ソースファイルを `java` コマンドに直接渡してもコンパイルと実行が一度に済みます。

```
# コンパイルしてから実行
javac HelloSwing.java
java HelloSwing

# JDK 11以降：ソースファイルを直接実行
java HelloSwing.java
```

IDEを使うなら、[IntelliJ IDEAでJava開発を始める手順](/tech/details/4377/)のとおりJDKを設定すれば、そのまま実行ボタンでウィンドウが開きます。

### ボタンとラベルを置く最小コード

ボタンを押すたびにラベルの回数表示が変わる、最小限のSwingアプリです。

```
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class HelloSwing {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Hello Swing");
            JLabel label = new JLabel("ボタンを押してください", JLabel.CENTER);
            JButton button = new JButton("押す");
            int[] count = {0};
            button.addActionListener(e -> label.setText(++count[0] + " 回押されました"));

            frame.add(label, BorderLayout.CENTER);
            frame.add(button, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(320, 160);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}
```

押さえるべき点は3つあります。まず、画面の組み立てを `SwingUtilities.invokeLater` の中で行っていること（理由は後述のスレッド規則の章）。次に、`JFrame` の中身の領域は既定で `BorderLayout` なので、`add` の第2引数で中央・下などの位置を指定できること。最後に `setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)` で、これを書かないとウィンドウを閉じてもプロセスが残ります。

### レイアウトマネージャの選び方

Swingでは座標を直接指定せず、レイアウトマネージャに配置を任せます。`JPanel` の既定は `FlowLayout`（通常は左から順に並べ、行全体は既定で中央寄せ）、`JFrame` の中身は `BorderLayout`（上下左右と中央の5領域）です。この2つに、同じ大きさのマス目に並べる `GridLayout`、縦か横の一列に並べる `BoxLayout` を加えれば、入力フォーム程度の画面は組めます。

行ごとに列幅が違う複雑なフォームは `GridBagLayout` で書けますが、制約の指定が長くなりがちです。その場合は `JPanel` を入れ子にして、外側を `BorderLayout`、内側を `GridLayout` のように分割したほうが読みやすいコードになります。`setLayout(null)` と `setBounds` で座標を固定する書き方は、フォントや画面の拡大率が変わると文字が欠けるため、新しく書くコードでは使いません。

## JTableで表を表示する：TableModelと並べ替え

### DefaultTableModelとAbstractTableModelの使い分け

`JTable` は表示だけを受け持ち、データは `TableModel` から受け取ります。手軽なのは `DefaultTableModel` で、[JDK 25のAPIドキュメント](https://docs.oracle.com/en/java/javase/25/docs/api/java.desktop/javax/swing/table/DefaultTableModel.html)によるとセルの値を「Vector of Vectors」で保持します。数行の確認用ならこれで足ります。ただし業務データの1件ごとに `addRow` で値を並べ直す必要があり、既定の `getColumnClass` は各列に `Object.class` を返すため、数値の列も型の情報を持ちません。

業務アプリでは `AbstractTableModel` を継承し、自分のデータ（下の例では `Employee` のリスト）をそのまま持たせるほうが扱いやすくなります。必須の実装は `getRowCount`・`getColumnCount`・`getValueAt` の3つです。`DefaultTableModel` を使う場合も、並べ替えと組み合わせるなら `getColumnClass` で列の型を返すことをAPIドキュメントが強く推奨しています。

### AbstractTableModelで社員一覧を出すコード

社員の一覧を表に出し、見出しのクリックで並べ替え、選択した行の社員名を下に表示する例です。`record` と `switch` 式を使っているので、JDK 16以降で動きます。

```
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

public class TableDemo {
    record Employee(String name, String dept, int age) {}

    // 行データを自前のリストで持ち、JTableには表示に必要な値だけを返す
    static class EmployeeModel extends AbstractTableModel {
        private final String[] columns = {"氏名", "部署", "年齢"};
        private final List<Employee> rows = new ArrayList<>();

        void add(Employee e) {
            rows.add(e);
            fireTableRowsInserted(rows.size() - 1, rows.size() - 1);
        }

        Employee get(int modelRow) {
            return rows.get(modelRow);
        }

        @Override public int getRowCount() { return rows.size(); }
        @Override public int getColumnCount() { return columns.length; }
        @Override public String getColumnName(int c) { return columns[c]; }

        @Override
        public Class<?> getColumnClass(int c) {
            // 年齢列をIntegerと宣言すると、ソートが数値順になり右寄せで表示される
            return c == 2 ? Integer.class : String.class;
        }

        @Override
        public Object getValueAt(int r, int c) {
            Employee e = rows.get(r);
            return switch (c) {
                case 0 -> e.name();
                case 1 -> e.dept();
                default -> e.age();
            };
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            EmployeeModel model = new EmployeeModel();
            model.add(new Employee("佐藤", "営業", 41));
            model.add(new Employee("鈴木", "開発", 9));
            model.add(new Employee("高橋", "総務", 103));

            JTable table = new JTable(model);
            table.setAutoCreateRowSorter(true);
            JLabel status = new JLabel("行を選択してください");
            table.getSelectionModel().addListSelectionListener(e -> {
                int viewRow = table.getSelectedRow();
                if (e.getValueIsAdjusting() || viewRow < 0) return;
                // 並べ替え後の表示行番号を、モデル側の行番号へ変換してから取り出す
                Employee emp = model.get(table.convertRowIndexToModel(viewRow));
                status.setText(emp.name() + "（" + emp.dept() + "）");
            });

            JFrame frame = new JFrame("JTable");
            frame.add(new JScrollPane(table), BorderLayout.CENTER);
            frame.add(status, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(360, 200);
            frame.setVisible(true);
        });
    }
}
```

`JTable` を `JScrollPane` に入れると、列見出しが自動で配置されます。スクロールペインを使わない場合も、`getTableHeader()` で取得した見出しを別途配置できます。`setAutoCreateRowSorter(true)`（Java SE 6で追加）を呼ぶだけで、見出しのクリックによる並べ替えが有効になります。年齢列で並べ替えると、表示は 9・41・103 の数値順になりました。`getColumnClass` を書かずに文字列として比較させると、同じ値が「103」「41」「9」の順に並びます。

### 並べ替え後の行番号とconvertRowIndexToModelによる変換

並べ替えを有効にした表では、選択行の番号をそのままモデルに渡すと、別の行のデータを取り出す不具合が起こります。上の例で年齢順に並べ替えたあと、表示上の0行目（鈴木）を選んでも、モデルの0行目は佐藤のままでした。`getSelectedRow()` が返すのは表示上の行番号なので、データを取り出す前に `convertRowIndexToModel` でモデル側の番号へ変換します。実際に並べ替え後の3行を変換すると、モデル側の番号は 1・0・2 と返りました。

この変換を忘れたコードは、並べ替えていない間は正しく動くため、テストで見落とされがちです。編集・削除ボタンのように「選んだ行のデータ」を使う処理には、必ずこの1行を挟みます。

## Swingのスレッド規則：EDTとSwingWorker

### SwingコンポーネントへのアクセスとEDTの原則

Swingのコードで最初に押さえるべきなのは、スレッドの規則です。`javax.swing` パッケージのドキュメントは「In general Swing is not thread safe.」と書いており、Oracleのチュートリアルも、スレッドセーフと明記された一部のメソッドを除き「All other Swing component methods must be invoked from the event dispatch thread.」としています。この規則を破ったプログラムは「たいていは正しく動くが、再現しにくい予測不能なエラーを起こす」とまで書かれています。

イベントディスパッチスレッド（EDT）は、ボタンのクリックなどのイベント処理と画面の描画を受け持つ1本のスレッドです。`main` メソッドはEDTではないので、画面の組み立ては `SwingUtilities.invokeLater` でEDTに渡します。ボタンの `ActionListener` はEDTで呼ばれるため、その中でラベルを書き換えるのは安全です。

### SwingWorkerによる時間のかかる処理の分離

逆に、EDTで時間のかかる処理（ファイル読み込み、DBアクセス、通信）を実行すると、その間は描画もクリック処理も止まり、ウィンドウが固まったように見えます。こうした処理は `SwingWorker` で別スレッドに移し、結果だけをEDTに戻します。

```
import java.awt.BorderLayout;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class WorkerDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("SwingWorker");
            JProgressBar bar = new JProgressBar(0, 100);
            JButton start = new JButton("開始");

            start.addActionListener(e -> {
                start.setEnabled(false);
                new SwingWorker<String, Integer>() {
                    @Override
                    protected String doInBackground() throws Exception {
                        for (int i = 1; i <= 10; i++) {
                            // 重い処理の代わり（EDTの外で動く）
                            Thread.sleep(200);
                            publish(i * 10);
                        }
                        return "完了";
                    }

                    @Override
                    protected void process(List<Integer> chunks) {
                        // EDTで呼ばれる。複数のpublishがまとめて届くので最後の値を使う
                        bar.setValue(chunks.get(chunks.size() - 1));
                    }

                    @Override
                    protected void done() {
                        // EDTで呼ばれる。get()で結果と例外を受け取る
                        try {
                            start.setText(get());
                        } catch (InterruptedException ex) {
                            Thread.currentThread().interrupt();
                            start.setText("中断");
                        } catch (ExecutionException ex) {
                            start.setText("失敗");
                        } catch (CancellationException ex) {
                            start.setText("キャンセル");
                        }
                    }
                }.execute();
            });

            frame.add(bar, BorderLayout.CENTER);
            frame.add(start, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        });
    }
}
```

`doInBackground` は別スレッドで動き、`process` と `done` はEDTで呼ばれます。`done` の中で `get()` を呼ぶと、処理が例外で終わった場合は `ExecutionException` として受け取れます。`publish` を短い間隔で何度も呼ぶと、複数の値が1回の `process` にまとめて渡されるため、進捗バーには最後の値を使います。JDK 25で実行したところ、200ミリ秒×10回の処理のあとでバーが100に達し、ボタンの表示が「完了」に変わりました。5回目の `publish(i * 10);` の直後に `if (i == 5) throw new IllegalStateException("検証用");` を加えた版では、バーが50で止まり「失敗」と表示されます。

## Swingアプリの配布：実行可能jarからjpackageまで

### 実行可能jarの作成と必要なモジュールの確認

配布先に、コンパイルに使ったJDKと同じかそれより新しい版のJava（`java.desktop` を含むもの）が入っていれば、実行可能jarを1つ渡せば済みます。JDK 25で普通にコンパイルしたクラスは古いJavaでは起動できないため、古い版へ配るときは `javac --release 17` のように対象の版を指定します。`jar` コマンドの `--main-class` で起動クラスを指定すると、受け取った側は `java -jar TableDemo.jar` で起動できます。続けて `jdeps --print-module-deps` を実行すると、そのjarが使うJDKのモジュールが分かります。前章の `TableDemo` では `java.base,java.desktop` と表示されました。

```
javac -d classes TableDemo.java
mkdir app
jar --create --file app/TableDemo.jar --main-class TableDemo -C classes .
java -jar app/TableDemo.jar

# 使っているJDKモジュールを調べる
jdeps --print-module-deps app/TableDemo.jar
```

Swingは `java.desktop` モジュールに含まれます。[jlink](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jlink.html) で必要なモジュールだけのランタイムを作るときにこれを入れ忘れると、Swingは動きません。`java.base` だけで作ったランタイムで上のjarを起動すると、「メイン・クラスTableDemoを初期化できません」と `java.lang.NoClassDefFoundError: javax/swing/table/TableModel` で止まりました。`--add-modules java.desktop` を指定して作り直したランタイムは81MBで、JDK 25本体（235MB）の約3分の1です。軽量化したDockerイメージや社内配布のランタイムでSwingが起動しないときは、まずこの点を疑います。

### jpackageによるJavaランタイム同梱アプリの作成

Javaが入っていないPCへ配るなら、`jpackage` でランタイムごと同梱したアプリにします。jpackageはJDK 14でJEP 343として試験導入され、JDK 16のJEP 392で正式な機能になりました。`--type app-image` を指定すると、インストーラーを作る前のアプリイメージ（macOSなら `.app`）だけを出力します。

```
jpackage --type app-image --name TableDemo \
  --input app --main-jar TableDemo.jar --main-class TableDemo \
  --add-modules java.desktop --dest dist
```

macOSのJDK 25で試すと、`--add-modules` を付けないアプリイメージは138MB、`--add-modules java.desktop` を付けると82MBでした。後者のランタイムには `java.desktop` が依存する `java.datatransfer`・`java.xml`・`java.prefs` と `java.base` が自動で入り、作った `TableDemo.app` はダブルクリックで表が開きます。

インストーラーの形式はOSごとに決まっていて、[JDK 25のパッケージングガイド](https://docs.oracle.com/en/java/javase/25/jpackage/packaging-overview.html)ではWindowsがexe・msi、macOSがpkg・dmg、Linuxがdeb・rpmです。[jpackageのマニュアル](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jpackage.html)には「Each format must be built on the platform it runs on, there is no cross-platform support.」とあり、Windows向けのmsiはWindows上で作る必要があります。Mac 1台で全OS向けのインストーラーをそろえることはできないので、配布先のOSごとにビルド環境（CIのWindows・Linuxランナーなど）を用意します。

## Swingでつまずきやすい症状と対処

### ウィンドウを閉じてもプロセスが残る原因と終了設定

`JFrame` の閉じるボタンの既定動作は、[JFrameのAPIドキュメント](https://docs.oracle.com/en/java/javase/25/docs/api/java.desktop/javax/swing/JFrame.html)にあるとおり「HIDE\_ON\_CLOSE」です。ウィンドウは隠れるだけでJVMは動き続けるため、IDEの停止ボタンを押すまでプロセスが残ります。JDK 25で `new JFrame().getDefaultCloseOperation()` を表示すると、`HIDE_ON_CLOSE` と同じ1が返りました。メインウィンドウには `setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)`（値は3）を指定します。

### CI・DockerでのHeadlessExceptionの原因とテスト構成

ディスプレイの無い環境では、`JFrame` のコンストラクターが `java.awt.HeadlessException` を投げます。`-Djava.awt.headless=true` を付けて前章の `TableDemo` を起動すると、`JFrame` の生成で同じ例外が出て止まりました。CIで画面のテストを回したいなら、表の並べ替えや入力チェックのロジックをSwingに依存しない通常のJavaクラスへ切り出し、そちらを単体テストする構成にします。`TableModel` はSwingのインタフェースですが、ウィンドウを生成しないモデルの処理はヘッドレス環境でもテストでき、`-Djava.awt.headless=true` の下でも `DefaultTableModel` への行追加は例外なく動きました。画面そのものの自動テストが必要な場合だけ、仮想ディスプレイ（LinuxのXvfbなど）を用意します。

### Swingの外観変更と高解像度ディスプレイへの対応

Swingの見た目は、コンポーネントではなくLook and Feelで決まります。既定のLook and FeelはOSによって変わり、macOSのJDK 25では `UIManager.getLookAndFeel().getName()` が「Mac OS X」を返しました。Look and Feelは `UIManager` で後から差し替えられるため、画面を作り直さなくても見た目を刷新できます。オープンソースのFlatLafを使うと、フラットデザインのライト・ダークテーマに1行で切り替えられます。最新版は[GitHubのリリース一覧](https://github.com/JFormDesigner/FlatLaf/releases)で2026年7月9日公開の3.7.2で、Mavenの座標は `com.formdev:flatlaf:3.7.2` です。Mavenを使わない場合は配布jarをソースと同じディレクトリに置き、`java -cp flatlaf-3.7.2.jar FlatHello.java` で実行できます。

```
import com.formdev.flatlaf.FlatLightLaf;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class FlatHello {
    public static void main(String[] args) {
        // コンポーネントを作る前にLook and Feelを切り替える
        FlatLightLaf.setup();
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("FlatLaf");
            frame.add(new JButton("FlatLafのボタン"));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        });
    }
}
```

JDK 25と27でこのコードを実行すると、FlatLafがネイティブライブラリを読み込む際に「A restricted method in java.lang.System has been called」という警告が出ます。動作には影響しませんが、起動オプションに `--enable-native-access=ALL-UNNAMED` を付けると警告が消えます。

高解像度ディスプレイで画面が小さく表示される問題は、JDK 9のJEP 263で対処済みです。WindowsとLinuxのHiDPI画面では、AWTとSwingのコンポーネントが自動で拡大・サイズ調整されます。JDK 8のまま運用しているアプリで文字が極端に小さい場合は、まずJDKの更新を検討してください。

## 2026年にSwingを選ぶかどうかの判断基準

### Swingが今も合理的な場面

Swingを選ぶ理由として最も強いのは、JDKだけで完結することです。JavaFXのように別配布のランタイムを管理する必要が無く、社内の業務ツールや計測・管理用の画面なら、JDKの入った環境へjarを置くだけで動きます。Javaの無いPCにも、前述のjpackageでランタイムを同梱して配れます。今回のmacOS上のTableDemoでは82MBでしたが、容量はOS・JDK・依存ライブラリによって変わります。JDK 26で削られたSwingのAPIがアプレット関連に限られたことが示すとおり、公開APIがほとんど削除されずに残っている点も、長期保守が前提のシステムでは大きな利点です。

大規模アプリでの実績もあります。JetBrainsの開発者向けドキュメントは、IntelliJベースのIDEのUIが主にSwingとAWTで作られていると説明しています。また、Compose Multiplatformのデスクトップ版には、Compose画面にSwing部品を埋め込む `SwingPanel` と、既存のSwing画面にComposeを埋め込む `ComposePanel` があり、Swing資産を残したまま段階的に画面を置き換える道が用意されています。

### Swingを選ぶべきでない場面

次の要件が必須なら、新規開発ではSwing以外の技術を選びます。

- スマートフォンやブラウザーでも同じ画面を使いたい。Swingはデスクトップ専用で、モバイルやWebには移せない。
- アニメーションや細かな見た目の作り込みが主眼。CSSによるスタイル指定を使いたい場合は、JavaFXやWeb技術を優先して検討する。
- LinuxのWayland環境でネイティブに動かす必要がある。Oracle JDK 27の認定構成でも、Waylandでは「Only Xwayland mode is supported.」で、ネイティブWayland対応はOpenJDKのProject Wakefieldで開発中の段階にある。

既存のSwingアプリが必要な機能と配布要件を満たしているなら、「古いから」という理由だけで全面移行しないことです。まずFlatLafへの差し替えで見た目の要件を満たせるか、jpackageで配布の手間を減らせるかを確かめ、それでも足りない画面だけを置き換えます。Swingの業務アプリの改修やWebへの移行を外部に頼む場合は、[業務用・Webアプリ開発の支援](/service/apps/business/)のように、既存画面の調査から引き受けるところを選ぶと移行範囲を絞りやすくなります。

## よくある質問

### Java Swingは廃止されたのですか？

廃止されていません。JDK 26（2026年3月）のJEP 504で削除されたのは `java.applet` パッケージや `javax.swing.JApplet` などアプレット関連のAPIで、Swing本体はJDK 27でも標準で含まれ、OpenJDKは引き続きサポートすると明言しています。

### Java Swingの後継は何ですか？

Oracleが後継として位置づけたのはJavaFXですが、JDK 11でJDKから分離され、現在はOpenJFXとして別に配布されています（最新はJavaFX 27）。SwingもJDK標準のまま残っているため、「後継に置き換えなければならない」状況ではありません。

### SwingとJavaFXの違いは何ですか？

SwingはJDKに標準で入っていて追加のライブラリ無しで動き、JavaFXはOpenJFXを別に入れる必要があります。見た目はSwingがLook and Feel、JavaFXがCSSで指定します。既存のSwingアプリの保守やJDKだけで動く社内ツールならSwing、見た目やアニメーションを重視する新規アプリならJavaFXが向いています。JavaFX側の導入手順は[JavaFX 27の導入手順とSwingとの違い](/column/details/2904/)にまとめています。

### javax.swingとjava.awtはどう使い分けるのですか？

ボタンや表などの部品は `javax.swing` のJ付きクラス（`JButton` など）に統一し、`java.awt` はレイアウト（`BorderLayout`）・イベント（`ActionEvent`）・描画（`Graphics`）の基盤として使います。`Button` と `JButton` を同じ画面に混ぜるのは避けます。

### Javaが入っていないPCでSwingアプリを動かすには？

JDK 16以降に付属する `jpackage` で、ランタイムを同梱したアプリやインストーラー（Windowsならexe・msi）を作ります。`--add-modules java.desktop` を指定すると同梱するランタイムを絞れます。インストーラーは配布先と同じOS上でしか作れません。

## 関連記事

- [JavaFXとは？廃止の真相とSwingとの違い・JavaFX 27の導入手順](/column/details/2904/)
- [Java SEとは？JDK・JRE・JVMの違いとOracle Java/OpenJDKのライセンス【2026年10月】](/column/details/3113/)
- [Java 26（JDK 26）の新機能10個のJEPと現在地｜サポート終了後の移行先と実機で確かめた挙動](/tech/details/11412/)
- [Java 27とは｜非LTSの半年運用とG1・オブジェクトヘッダ既定化の影響](/tech/details/17712/)
- [IntelliJ IDEAでJava開発を始める手順｜インストール・JDK設定・日本語化【2026.2対応】](/tech/details/4377/)

---

出典: [Java Swingとは？廃止されたのはアプレットAPIだけ｜使い方・JTable・jpackage配布](<https://www.issoh.co.jp/column/details/2903/>)（株式会社一創）
