24 人が閲覧(直近 30 日) アプリ

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

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メーリングリストで示された削除範囲は次のとおりです。

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

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

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

JEP 504の告知記事(inside.java、2025年12月3日)は、残りの部分について「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のプロジェクトページ(2026年10月22日更新)で対象になっている10件はValue Objects(プレビュー)や構造化並行性などで、Swingに関わるJEPはありません。JDK 26の変更全体はJava 26(JDK 26)の新機能10個のJEP、JDK 27はJava 27の変更点と非LTSの運用で整理しています。

「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との違いで整理しています。

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サポート・ロードマップで2030年9月までとされています。これはOracleのサポート契約の話で、Temurinなど他の配布元の保守期間は各配布元の方針で決まります。JDKの種類やライセンスの違いはJava SEとJDK・JRE・JVMの違いを参照してください。

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

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

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

IDEを使うなら、IntelliJ IDEAでJava開発を始める手順のとおり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ドキュメントによるとセルの値を「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 で必要なモジュールだけのランタイムを作るときにこれを入れ忘れると、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のパッケージングガイドではWindowsがexe・msi、macOSがpkg・dmg、Linuxがdeb・rpmです。jpackageのマニュアルには「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ドキュメントにあるとおり「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のリリース一覧で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アプリ開発の支援のように、既存画面の調査から引き受けるところを選ぶと移行範囲を絞りやすくなります。

よくある質問

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との違いにまとめています。

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上でしか作れません。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.09 テックブログ エディオン不正アクセス第2報:カード番号の対象確認とログ・決済画面の改ざん対策
  2. 2026.11.09 テックブログ LANSCOPE不正アクセス第2報:製品への影響と偽連絡の確認、停止中のライセンス認証の窓口
  3. 2026.11.10 テックブログ Claude Fable 5.2とは:料金・破壊的変更4点とFable 5.1からの移行手順【2026年11月】
  4. 2026.11.09 テックブログ ステップゴルフ不正アクセス:口座情報の対象確認と偽連絡・引き落としの注意点
  5. 2026.11.10 テックブログ ニトリの情報漏洩:最大3,762万件の対象確認となりすまし連絡の見分け方

RELATED POSTS 関連記事

目次