Java SE · For CS students

Build and Ship a Desktop App with Java SE: A Hands-On Guide for CS Students

Khairul Alam·October 2026·~35 min read
Java SEDesktop appsSwingJavaFXjpackageStudents

Most computer science courses teach Java with programs that print text. Real software has windows, buttons, files and an installer. This guide takes you from an empty folder to a working desktop app, then to something you can email to a friend or put on GitHub as a download. You will write the app with plain Java SE, test the logic, and package it with the tools that ship inside the JDK. A reading map near the start shows which topics of the Deitel and Deitel book (Java How to Program) line up with each step.

The whole post in 60 seconds
  • A desktop app is event-driven: a loop waits for clicks and keys, and your code reacts. That idea is the real lesson.
  • Swing is already inside the JDK, so a first app needs no downloads. JavaFX is the modern alternative, but it is a separate download.
  • Keep logic (save, load, rules) in plain classes and screens in others. Then you can test the logic with JUnit.
  • Never do slow work on the event thread, or the window freezes. Use SwingWorker (or Platform.runLater in JavaFX).
  • Package with jar for a quick share, and with jpackage for a real installer that includes Java, so users do not need to install it.
  • You must build the installer on each operating system you target. A GitHub Actions matrix can do that for you.
  • A project ladder and a portfolio checklist are at the end.
Contents
  1. What you will build, and why desktop still matters
  2. Using the Deitel and Deitel book with this guide
  3. Set up your tools
  4. How a desktop app works: events
  5. Your first window
  6. Build the Notes app, step by step
  7. Keep the window responsive
  8. Swing or JavaFX?
  9. Structure the project and test it
  10. Polish: look, settings and accessibility
  11. Package a JAR
  12. Ship it: jlink and jpackage
  13. Publish releases and automate builds
  14. Common mistakes
  15. Projects to practise, and a portfolio checklist
  16. Takeaways and glossary

1. What you will build, and why desktop still matters

We will build Notes, a small desktop app. You type a note, press Enter, and it appears in a list. You can delete a note. When you close the app and open it again, your notes are still there, because they are saved to a file in your home folder. It is small enough to finish in one sitting, and it touches almost everything a bigger app needs: a window, buttons, lists, input, files, error messages, and a way to package it.

Why bother with desktop when so much software lives on the web or on phones? Because it teaches things that other platforms hide behind frameworks:

Desktop apps are also still everywhere: IDEs, music tools, lab equipment software, internal business tools, and teaching tools. Java is a good fit because one source code runs on all three major systems.

Java SE, JDK and JRE in one minute

TermMeaning
Java SEJava Standard Edition: the core language and libraries, including the GUI toolkit Swing. This is what you use for desktop apps.
JDKJava Development Kit: the compiler (javac), the runtime and the tools (jar, jlink, jpackage). You install this to write programs.
JRE / runtimeWhat runs a compiled program. Users used to install it separately. Today you can bundle it inside your app (section 12).
LTSLong-term support release. Versions 17 and 21 are LTS releases. Pick one for coursework so your code does not move under you.

2. Using the Deitel and Deitel book with this guide

Java How to Program by Paul Deitel and Harvey Deitel (published by Pearson) is one of the most widely used Java textbooks in university courses. It teaches by showing complete, runnable programs, and it ships its source code online. This guide is a project that sits on top of that kind of course: the book explains each language feature, and this guide shows how they join together in one application.

Which edition? The publisher’s site (deitel.com) currently lists Java How to Program 11th edition in an Early Objects and a Late Objects version, and a newer 12th edition with an “objects-natural” approach. Chapter numbers and titles differ by edition, so use the table of contents of your edition. The topic names below are the stable part.

Step in this guideJava ideaLook for this topic in the book
The Notes model and screensClasses, objects, methods, constructorsIntroduction to classes and objects; methods in depth
The list of notesCollections, ArrayList, genericsArrays and ArrayLists; generic collections
Splitting logic from the screenInterfaces, composition, inheritanceInheritance; polymorphism; interfaces; composition vs inheritance
Reading and saving notesFiles, paths, streamsFiles, input and output streams (the NIO classes)
“Could not save” messagesExceptions, try and catchException handling
Buttons, lists and layoutsGUI components and event handlingThe JavaFX GUI chapters in recent editions; the optional Swing GUI chapters
Lambdas such as e -> addNote()Lambdas and functional interfacesLambdas and streams
Not freezing the windowThreads and concurrencyConcurrency
Storing data in a database (an exercise)JDBCDatabase access with JDBC (online chapter in some editions)

How to get the most out of the book

Recent editions teach the GUI with JavaFX and keep Swing as optional or online material, while older editions teach Swing. This guide builds in Swing first because it needs nothing extra to install, then shows JavaFX side by side. The ideas (components, layouts, events) carry over almost unchanged.

3. Set up your tools

  1. Install a JDK (version 21 is a good choice; 17 also works). Free builds include Eclipse Temurin from Adoptium, and the builds from Oracle, Microsoft, Amazon and others. Your course may name one.
  2. Pick an editor. IntelliJ IDEA Community, NetBeans, Eclipse or VS Code with the Java extension are all fine. An IDE helps with errors and auto-complete. We will also use the terminal, so you understand what the IDE does for you.
  3. Check the install in a terminal:
java -version
javac -version
jpackage --version

If java works but javac does not, you installed a runtime but not the JDK. Install the JDK. If the commands are not found at all, the JDK’s bin folder is not on your PATH.

Project layout

notes-app/
  src/
    notes/
      NoteStore.java      <- saves and loads notes (no GUI code)
      NotesApp.java       <- the window
  test/                   <- unit tests, later
  out/                    <- compiled classes (created by javac)
  dist/                   <- the finished JAR (created later)

The folder name under src must match the package line at the top of each file. A file starting with package notes; must live in src/notes/.

4. How a desktop app works: events

A console program runs from the first line to the last, and then it ends. A desktop program does something different. After it builds the window, it starts an event loop: a loop that waits for the user to do something. Each click, key press or mouse move becomes an event placed in a queue. A single thread, the event dispatch thread (EDT), takes events from the queue one by one and calls the listener you registered for that event. Your code runs only inside those listeners.

Userclick or key Event queuewaiting events Event threadtakes one event Listeneryour code Repaintnew screen Everything above runs on one thread. If your listener is slow, the whole window waits.
The key rule of GUI programming: keep listeners short, because the same thread also draws the window.

Analogy. A cafe with one waiter. Customers (events) line up, and the waiter serves them one at a time. If the waiter goes to the kitchen to cook a three-hour stew, nobody else gets served, even to be handed a glass of water. That frozen cafe is what a frozen window looks like.

5. Your first window

Save this as src/notes/Hello.java:

package notes;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.FlowLayout;

public class Hello {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {                    // build the window on the event thread
            JFrame frame = new JFrame("Hello");
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.setLayout(new FlowLayout());

            JButton button = new JButton("Click me");
            int[] clicks = {0};
            button.addActionListener(e -> button.setText("Clicked " + (++clicks[0]) + " times"));

            frame.add(button);
            frame.setSize(300, 120);
            frame.setLocationRelativeTo(null);               // centre on screen
            frame.setVisible(true);
        });
    }
}

Compile and run it from the project folder:

mkdir -p out
javac -d out src/notes/Hello.java
java -cp out notes.Hello

On Windows PowerShell, use mkdir out if it does not exist and the same javac and java lines. Here is what each part does:

6. Build the Notes app, step by step

Step 1: the storage class (no GUI)

We begin with the part that has no windows in it. It saves a list of notes to a text file, one line per note, and loads them again. Because it does not touch Swing, it is easy to test, and later you could swap it for a database without changing the screen.

package notes;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

/** Loads and saves notes as one line of text per note. */
public class NoteStore {

    private final Path file;

    public NoteStore(Path file) {
        this.file = file;
    }

    /** The normal location: a hidden folder in the user's home directory. */
    public static NoteStore inUserHome() {
        Path dir = Path.of(System.getProperty("user.home"), ".notes-app");
        return new NoteStore(dir.resolve("notes.txt"));
    }

    public List<String> load() throws IOException {
        if (Files.notExists(file)) {
            return new ArrayList<>();                       // first run: no file yet
        }
        return new ArrayList<>(Files.readAllLines(file));
    }

    public void save(List<String> notes) throws IOException {
        Files.createDirectories(file.getParent());           // make the folder if needed
        Files.write(file, notes);
    }
}

Notice three decisions that matter for real apps. The file lives in the user’s home folder, not next to the program, because installed apps are often in read-only places. The code uses Path and Files, which handle Windows backslashes and Linux slashes for you. And the methods declare throws IOException, so the caller must decide what to show the user when something fails.

Step 2: the window

package notes;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.io.IOException;
import java.util.Collections;

public class NotesApp {

    private final NoteStore store = NoteStore.inUserHome();
    private final DefaultListModel<String> model = new DefaultListModel<>();   // the data behind the list
    private final JTextField input = new JTextField();
    private final JLabel status = new JLabel(" ");

    private void show() {
        JFrame frame = new JFrame("Notes");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JList<String> list = new JList<>(model);
        JButton add = new JButton("Add");
        JButton delete = new JButton("Delete");

        // Layout: status on top, list in the middle, input row at the bottom
        JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
        buttons.add(add);
        buttons.add(delete);

        JPanel bottom = new JPanel(new BorderLayout(6, 6));
        bottom.add(input, BorderLayout.CENTER);
        bottom.add(buttons, BorderLayout.EAST);

        JPanel root = new JPanel(new BorderLayout(8, 8));
        root.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        root.add(status, BorderLayout.NORTH);
        root.add(new JScrollPane(list), BorderLayout.CENTER);
        root.add(bottom, BorderLayout.SOUTH);

        // Events
        add.addActionListener(e -> addNote());
        input.addActionListener(e -> addNote());              // pressing Enter in the text field
        delete.addActionListener(e -> deleteNote(list.getSelectedIndex()));

        loadNotes();

        frame.setContentPane(root);
        frame.setSize(420, 360);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        input.requestFocusInWindow();
    }

    private void addNote() {
        String text = input.getText().strip();
        if (text.isEmpty()) {
            return;                                           // ignore blank notes
        }
        model.addElement(text);
        input.setText("");
        saveNotes();
    }

    private void deleteNote(int index) {
        if (index < 0) {
            status.setText("Select a note first.");
            return;
        }
        model.remove(index);
        saveNotes();
    }

    private void loadNotes() {
        try {
            store.load().forEach(model::addElement);
            status.setText(model.size() + " notes");
        } catch (IOException ex) {
            status.setText("Could not read notes: " + ex.getMessage());
        }
    }

    private void saveNotes() {
        try {
            store.save(Collections.list(model.elements()));
            status.setText(model.size() + " notes saved");
        } catch (IOException ex) {
            status.setText("Could not save: " + ex.getMessage());
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new NotesApp().show());
    }
}
javac -d out src/notes/NoteStore.java src/notes/NotesApp.java
java -cp out notes.NotesApp

Try it: add a few notes, close the window, run it again. They come back. You can look at the saved file in the .notes-app folder in your home directory.

What to notice in this code

Try these changes (each one teaches something)
  • Add a Clear all button, and ask for confirmation with JOptionPane.showConfirmDialog.
  • Make double-click on a note open it in a dialog for editing.
  • Show the note count in the window title.
  • Sort the notes alphabetically with a stream: list.stream().sorted().

7. Keep the window responsive

Our file is tiny, so reading it takes microseconds. Real apps load large files, call web services or crunch numbers, and that can take seconds. If you do it inside a listener, you are the three-hour stew in the cafe: the window cannot repaint, buttons look dead, and the operating system may label the program “not responding”.

The rules are simple:

  1. Slow work goes on a background thread.
  2. Touching the UI goes on the event thread.

Swing gives you SwingWorker to do both. It runs doInBackground on a worker thread and done back on the event thread:

// needs: import javax.swing.SwingWorker; import java.util.List;
private void loadNotesInBackground() {
    status.setText("Loading...");
    new SwingWorker<List<String>, Void>() {
        @Override
        protected List<String> doInBackground() throws Exception {
            return store.load();                              // background thread: no UI code here
        }

        @Override
        protected void done() {                               // event thread again: safe to touch the UI
            try {
                get().forEach(model::addElement);
                status.setText(model.size() + " notes");
            } catch (Exception ex) {
                status.setText("Could not load: " + ex.getMessage());
            }
        }
    }.execute();
}
SwingJavaFX
The UI thread is calledEvent dispatch thread (EDT)JavaFX Application Thread
Start the UISwingUtilities.invokeLaterApplication.launch and start
Run something on the UI threadSwingUtilities.invokeLaterPlatform.runLater
Background task helperSwingWorkerTask and Service

The concept (never block the UI thread; only touch components on it) is the same in both. It is also the same rule that Android has for its main thread.

8. Swing or JavaFX?

SwingJavaFX
Where it comes fromPart of the JDK (java.desktop module)Separate project (OpenJFX), not in the JDK since Java 11
SetupNoneAdd the library with Maven or Gradle, or download the SDK
LookDated by default, but can be modernised (for example with FlatLaf)Modern by default, styled with CSS
Layout and design toolsLayout managers in code; IDE form designersLayouts in code or FXML files; Scene Builder drag-and-drop tool
Graphics, animation, mediaBasicStrong: 2D, 3D, animation, charts, media
PackagingSimple: a normal JARMore steps: the libraries must be on the module path
Best forSmall tools, coursework, quick delivery, mature codebasesNew apps with rich visuals, if you accept the extra setup

The same Notes window in JavaFX looks like this. Compare it with the Swing version: a Stage replaces JFrame, a Scene holds the content, and setOnAction replaces addActionListener.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class HelloFx extends Application {
    @Override
    public void start(Stage stage) {
        Label label = new Label("0 clicks");
        Button button = new Button("Click me");
        int[] clicks = {0};
        button.setOnAction(e -> label.setText(++clicks[0] + " clicks"));

        stage.setScene(new Scene(new VBox(10, label, button), 240, 120));
        stage.setTitle("Hello JavaFX");
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

The easiest way to run JavaFX is through Maven. Add the library and the run plugin to pom.xml (take the version numbers from openjfx.io), then run mvn javafx:run:

<dependencies>
  <dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>VERSION</version>
  </dependency>
</dependencies>
<build>
  <plugins>
    <plugin>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-maven-plugin</artifactId>
      <version>VERSION</version>
      <configuration><mainClass>HelloFx</mainClass></configuration>
    </plugin>
  </plugins>
</build>
Which should you learn first?

If your book and course teach JavaFX, follow them: it is the better skill for new work. If you want the shortest path from zero to an installer, build with Swing first: nothing extra to install, and the packaging steps in sections 11 and 12 are simpler. Either way, the event loop, listeners, layouts and threading rules transfer completely.

9. Structure the project and test it

A common beginner problem is a single enormous class where the buttons, the file code and the rules are tangled together. You cannot test any of it without opening a window. The fix is the same separation we used for NoteStore:

A good test: could you run the model code from a plain main method that shows no window? If yes, you can test it.

A first unit test with JUnit 5

JUnit is the standard Java test library. This test saves notes to a temporary folder and reads them back. The @TempDir parameter gives each test its own clean folder, which is deleted afterwards, so the test never touches your real notes.

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;

class NoteStoreTest {

    @Test
    void savesAndReloadsNotes(@TempDir Path dir) throws IOException {
        NoteStore store = new NoteStore(dir.resolve("notes.txt"));

        store.save(List.of("buy milk", "call mum"));

        assertEquals(List.of("buy milk", "call mum"), store.load());
    }

    @Test
    void missingFileMeansNoNotes(@TempDir Path dir) throws IOException {
        NoteStore store = new NoteStore(dir.resolve("does-not-exist.txt"));

        assertTrue(store.load().isEmpty());
    }
}

The easiest way to run it is a build tool. Maven and Gradle download JUnit for you and run mvn test or gradle test. If you want the long version of testing (the test pyramid, JUnit 4 and 5, mocking, UI tests and TDD), read Testing Android Apps. It is written for Android, but the JUnit parts apply to any Java project.

Testing the window itself is harder. Libraries such as AssertJ Swing (for Swing) and TestFX (for JavaFX) can click buttons from tests. A small number of those, plus many tests of the model, is a good balance.

10. Polish: look, settings and accessibility

A better look

Swing defaults to a plain cross-platform look. One line changes it to the look of the current operating system, and a free library called FlatLaf gives it a modern, flat style:

// Option A: the system look (call this before creating any window)
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

// Option B: FlatLaf (add the com.formdev:flatlaf library first)
FlatLightLaf.setup();

Remember user settings

Window size, last folder, preferred theme. The simplest built-in tool is java.util.prefs.Preferences, which stores values in the right place on each operating system:

Preferences prefs = Preferences.userNodeForPackage(NotesApp.class);
int width = prefs.getInt("width", 420);       // second argument is the default
prefs.putInt("width", frame.getWidth());

Make it usable for everyone

11. Package a JAR

A JAR file is a ZIP archive of compiled classes plus a small manifest that names the class to start. It is the simplest thing you can hand to someone who already has Java.

mkdir -p dist
jar --create --file dist/notes.jar --main-class notes.NotesApp -C out .
java -jar dist/notes.jar

Explaining the command: --file is the archive to create, --main-class writes the manifest entry that java -jar reads, and -C out . means “change into out and add everything”. If you use a library such as FlatLaf, the JAR needs it too, either on the class path (java -cp dist/notes.jar:lib/flatlaf.jar ...) or bundled with a build plugin.

Why a JAR alone is not a good way to ship
  • The user must already have a suitable Java installed, and the right version.
  • Double-clicking a JAR works on some systems and not on others.
  • There is no icon, no Start-menu entry, and no uninstaller.

A JAR is still useful for classmates, graders and developer tools. For everyone else, build an installer.

12. Ship it: jlink and jpackage

Two tools inside the JDK do the heavy work.

.javasource .classjavac .jarjar jpackage+ jlink runtime Installer.msi .dmg .deb Each arrow is one command. The last step must run on the operating system you are building for.
From source code to a double-clickable installer, using only tools from the JDK.

Step 1: find out which modules you need

jdeps --print-module-deps --ignore-missing-deps dist/notes.jar
# prints: java.base,java.desktop

Java is split into modules. A Swing app needs java.desktop, plus the always-present java.base. Everything else (database drivers, the compiler, XML tools) can be left out, which keeps your download small.

Step 2: build the installer

Put the JAR (and any libraries) in a folder, here dist. Then run the command for your system. The pieces in common are the app name, version, input folder and main class.

# macOS
jpackage --type dmg --name Notes --app-version 1.0.0 --vendor "Your Name" \
  --input dist --main-jar notes.jar --main-class notes.NotesApp \
  --add-modules java.desktop --icon packaging/notes.icns --dest installers

# Windows (PowerShell: use ` at the end of a line instead of \)
jpackage --type msi --name Notes --app-version 1.0.0 --vendor "Your Name" `
  --input dist --main-jar notes.jar --main-class notes.NotesApp `
  --add-modules java.desktop --icon packaging\notes.ico `
  --win-menu --win-shortcut --win-dir-chooser --dest installers

# Linux (Debian, Ubuntu)
jpackage --type deb --name notes --app-version 1.0.0 --vendor "Your Name" \
  --input dist --main-jar notes.jar --main-class notes.NotesApp \
  --add-modules java.desktop --icon packaging/notes.png \
  --linux-shortcut --dest installers

Useful options: --type app-image builds a plain folder with the app and its runtime (no installer tools needed, good for a first test), --java-options "-Xmx512m" passes settings to the JVM, and --runtime-image uses a runtime you made yourself with jlink. When your project uses extra libraries, put them in the --input folder and list them in the JAR’s class path, or use the module path options described in the jpackage documentation.

You build on…You getYou must also have
Windows.msi or .exe installerThe WiX Toolset (see the jpackage documentation for the version your JDK supports)
macOS.dmg or .pkgXcode command-line tools. The version number must start with 1 or higher (not 0.x)
Linux.deb or .rpmdpkg (Debian family) or rpmbuild (Red Hat family)

No cross-compiling. jpackage builds for the system it runs on. To make a Windows installer you run it on Windows, and a Mac installer on a Mac. If you do not own all three, use a build service (next section).

What your users will see

13. Publish releases and automate builds

Put the project on GitHub and attach your installers to a Release: a page with a version tag, release notes and downloadable files. Anyone can then download your app from a stable link, and it makes a strong item for a CV or portfolio.

Because jpackage must run on each target system, a GitHub Actions matrix job is the practical answer: it runs the same steps on a Mac, a Windows and a Linux machine in the cloud and saves each installer.

# .github/workflows/package.yml
name: Package installers
on:
  push:
    tags: ["v*"]            # run when you push a tag such as v1.0.0

jobs:
  package:
    strategy:
      matrix:
        include:
          - { os: macos-latest,   type: dmg }
          - { os: windows-latest, type: msi }
          - { os: ubuntu-latest,  type: deb }
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        shell: bash
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "21"
      - name: Compile and make the JAR
        run: |
          mkdir -p out dist installers
          javac -d out src/notes/*.java
          jar --create --file dist/notes.jar --main-class notes.NotesApp -C out .
      - name: Build the installer
        run: |
          jpackage --type ${{ matrix.type }} --name Notes --app-version 1.0.0 \
            --input dist --main-jar notes.jar --main-class notes.NotesApp \
            --add-modules java.desktop --dest installers
      - uses: actions/upload-artifact@v4
        with:
          name: installer-${{ matrix.os }}
          path: installers/*

Check that each runner image has the tools it needs (for example WiX on Windows), and read the logs of your first run carefully, because the first run on a new system usually needs a fix or two. To publish the files on the Release page, add a step that uploads them to the release for the tag.

Things not to use

14. Common mistakes

MistakeWhat happensFix
Long work inside a listenerThe window freezes, then the OS says “not responding”Move it to SwingWorker (or a JavaFX Task)
Changing components from another threadRandom glitches or exceptions that appear once in a hundred runsUpdate only on the event thread: invokeLater or Platform.runLater
Building the UI outside invokeLaterWorks on your machine, fails on someone else’sStart every Swing app from SwingUtilities.invokeLater
Hard-coded file paths such as C:\Users\me\notes.txtBreaks on any other computerUse user.home and Path.of, or Preferences
Saving next to the programFails when installed in a read-only folderStore data in the user’s home or app-data folder
Pixel positions (setBounds) everywhereBroken layouts on other screens and fontsUse layout managers
One giant main classNothing is testable, and every change is riskySplit model and view, and test the model
Swallowing exceptions (catch (Exception e) {})Failures are silentShow a message, or at least log the error
Only testing from the IDEThe app works in the IDE and fails after packagingRun the installed app on a clean machine or account
Forgetting EXIT_ON_CLOSEThe window closes but the process stays aliveSet the default close operation
Ignoring high-resolution screensBlurry or tiny UIUse a recent JDK, vector or large icons, and layout managers

15. Projects to practise, and a portfolio checklist

Learning sticks when you build things that grow. Each project adds one new idea:

#ProjectNew ideas
1Counter and calculatorButtons, labels, layouts, simple events
2Notes (this guide)Lists, files, exceptions, packaging
3To-do list with due dates and filtersModels, sorting, java.time dates, tables
4Image viewer or simple drawing toolCustom painting, mouse events, file dialogs
5Expense tracker with a chartJDBC and SQLite, or CSV files; charts
6Chat client for a small server you also writeSockets, threads, background updates to the UI
7Game of life, Sudoku or a board gameTimers, animation, game loops, state machines
Portfolio checklist (so a stranger can try your app in two minutes)
  • A README with a one-line description, a screenshot, how to run from source, and how to install.
  • A Release with installers for at least one system, and a note about any security warning.
  • A clear folder structure and a few unit tests of the model.
  • A short “what I learned” section: the hardest bug, and what you would do next.
  • A licence file, so others know if they may reuse your code.

16. Takeaways and glossary

TermIn plain words
EventSomething the user or system did: a click, a key press, a timer firing.
ListenerCode you register to run when a particular event happens.
Event dispatch thread (EDT)The one thread that handles Swing events and drawing.
Layout managerAn object that positions components for you and adapts to the window size.
LambdaA short, unnamed function, often used as a listener.
SwingWorkerA Swing helper that runs work in the background and returns the result to the event thread.
JARA ZIP-style archive of compiled Java classes and a manifest.
ModuleA named part of Java or of your app (for example java.desktop).
jlinkThe JDK tool that builds a small custom Java runtime.
jpackageThe JDK tool that builds a native installer with the runtime included.
Code signingProving who made an installer, so operating systems warn less.
CI matrixA build that runs the same steps on several operating systems.
← Back to all writing