Build a Java Swing Click Counter

Build a Java Swing click counter while learning core programming concepts.

Introduction

30 Second Summary

Small apps often start with a number that changes when someone clicks a button. The code behind that simple interaction must keep the stored value in sync with the screen.

In this project, you will build a console student summary with Java before creating a desktop click counter with Swing. The finished programs show how readable names connect stored values to visible behavior.

What You'll Build

You will launch a desktop counter where Increment changes Count: 0 into a rising total while Reset restores the starting value.

By the end of this project, you'll have:

  • A runnable console student summary that displays meaningful values for all eight primitive types plus a String reference.
  • A method-driven score calculation you can trace from a created object to Final score: 85.
  • A working desktop counter you can demonstrate by raising the total with Increment before restoring it with Reset.
  • An optional Secret Mission: add a Decrement button that protects zero as the minimum count.

Are there any prerequisites?

You need a Windows computer with permission to run Eclipse IDE for Java Developers. A working Eclipse Temurin JDK is checked during setup, with installation guidance available if needed. No programming experience is required.

Before We Start

Before the hands-on work begins, this checkpoint locks in what you are building and why it matters. The finished project combines a console Java fundamentals program with a Swing click counter.

Verify Java and Create the Eclipse Project

Before Eclipse can compile your first source file, it needs a full JDK. The checked entry supplies the compiler.

The workshop targets Eclipse Temurin JDK 25.0.4.1+1. A successful starter run confirms that Eclipse can use it.

In this step, get ready to:
  • Confirm that Eclipse uses a full JDK.
  • Create the JavaFundamentalsWorkshop project.
  • Produce the first Console output from JavaFundamentalsDemo.java.
Verify the JDK configuration

The checked Java environment becomes the default for running programs in Eclipse. This check keeps compiler problems from interrupting the workshop.

  • Press the Windows key to open Windows search.
  • Type Eclipse into the search bar.
  • Press Enter to open Eclipse.
  • Click Window in the top menu.
  • Click Preferences....
  • Expand Java in the preferences list.
  • Select Installed JREs.

The checked entry is Eclipse's default Java environment. Match the list on your screen to one of these outcomes.

✔️ I see version 25.0.4.1+1 or higher

  • Confirm that the checked entry is Eclipse Temurin JDK 25.0.4.1+1 or another full JDK.
  • Click Apply and Close.

You have cleared the setup hurdle. Eclipse now has the compiler needed for your workshop project.

ⓧ I see an older version

The workshop uses Eclipse Temurin JDK 25. Installing that release gives Eclipse the expected compiler.

  • Close the Preferences window.
  • Press the Windows key to open Windows search.
  • Type Windows PowerShell into the search bar.
  • Press Enter to open Windows PowerShell.

Windows may request permission after the installation begins. This request allows the JDK files to be added to your computer.

  • Install Eclipse Temurin JDK 25 by running this command:
winget install EclipseAdoptium.Temurin.25.JDK

What does this command do?

  • WinGet installs the Eclipse Temurin JDK 25 package.
  • The installed JDK supplies the compiler that Eclipse needs.

The download can take several minutes. A quiet installer during that time does not mean it has stopped.

  • Approve the Windows permission request if it appears.
  • Wait for PowerShell to report that the installation has finished.
  • Close Eclipse.
  • Press the Windows key to return to Windows search.
  • Type Eclipse into the search bar.
  • Press Enter to reopen Eclipse.

Restarting Eclipse refreshes the Java environments available to the workspace.

  • Click Window in the top menu.
  • Click Preferences....
  • Expand Java in the preferences list.
  • Select Installed JREs.
  • Check the new full JDK entry.
  • Click Apply and Close.

You have recovered the workshop toolchain. Eclipse now uses a checked full JDK.

WinGet or the new JDK missing?

Help me configure the installed JDK in Eclipse.

  • Use the Windows MSI from the official Adoptium installation page if WinGet is unavailable.
  • Ask your workshop instructor to run the cached installer if your Windows account cannot install software.

ⓧ No full JDK is listed

Eclipse needs a full JDK before it can compile the starter class. Installing Eclipse Temurin adds the required compiler.

  • Close the Preferences window.
  • Press the Windows key to open Windows search.
  • Type Windows PowerShell into the search bar.
  • Press Enter to open Windows PowerShell.

Windows may request permission after the installation begins. This request allows the JDK files to be added to your computer.

  • Install Eclipse Temurin JDK 25 by running this command:
winget install EclipseAdoptium.Temurin.25.JDK

What does this command do?

  • WinGet installs the Eclipse Temurin JDK 25 package.
  • The package provides Eclipse with a Java compiler.

The download can take several minutes. A quiet installer during that time does not mean it has stopped.

  • Approve the Windows permission request if it appears.
  • Wait for PowerShell to report that the installation has finished.
  • Close Eclipse.
  • Press the Windows key to return to Windows search.
  • Type Eclipse into the search bar.
  • Press Enter to reopen Eclipse.

Restarting Eclipse refreshes the Java environments available to the workspace.

  • Click Window in the top menu.
  • Click Preferences....
  • Expand Java in the preferences list.
  • Select Installed JREs.
  • Check the new full JDK entry.
  • Click Apply and Close.

The missing compiler is now in place. Eclipse has a checked full JDK for compiling Java source files.

Installation still blocked?

Help me diagnose why Eclipse cannot find my installed JDK.

  • Use the Windows MSI from the official Adoptium installation page if WinGet is unavailable.
  • Ask your workshop instructor to run the cached installer if your Windows account cannot install software.
Create the project and starter class

A Java project gives Eclipse a defined place for source files. Its default source folder holds the classes that Eclipse compiles.

  • Click File in the top menu.
  • Select New.
  • Select Project....
  • Select Java Project in the project wizard.
  • Enter JavaFundamentalsWorkshop in the Project name field.
  • Keep Use default JRE selected.
  • Click Finish.
  • Select the default source folder beneath JavaFundamentalsWorkshop in the project tree.

Good progress. The JavaFundamentalsWorkshop project now has a source location for your code.

A public Java class uses the same spelling as its source file. This link helps Eclipse find the class you want to run.

  • Click File in the top menu.
  • Select New.
  • Select Class.
  • Enter JavaFundamentalsDemo in the class name field.
  • Leave the Package field blank.
  • Select the public static void main(String [] args) method stub.
  • Click Finish.

Eclipse opens JavaFundamentalsDemo.java in the editor. Its main method marks the program's starting point.

  • Press Ctrl+A inside the JavaFundamentalsDemo.java editor.
  • Replace the selected contents by pasting this starter class:
public class JavaFundamentalsDemo {
    public static void main(String[] args) {
        System.out.println("Java workshop ready.");
    }
}

What does this code do?

  • The public JavaFundamentalsDemo class matches the JavaFundamentalsDemo.java file name.
  • The main method gives Eclipse a starting point.
  • The System.out.println statement sends one line to the Console view.
  • Press Ctrl+S to save JavaFundamentalsDemo.java.
  • Confirm that the editor shows no red error markers.

Seeing red error markers?

  • Confirm that the public class name matches JavaFundamentalsDemo.java exactly.
  • Keep the print statement inside the main method braces.
  • Check that the print statement ends with a semicolon.

Help me fix the starter class without adding new features.

✔️ Awesome, I've got everything!

Your saved starter class matches the workshop checkpoint. It is ready to compile.

ⓧ I'd like to double check the full code

public class JavaFundamentalsDemo {
    public static void main(String[] args) {
        System.out.println("Java workshop ready.");
    }
}
Run the first Java application

Running the class tests the path from source code to visible output. A successful result confirms that Eclipse can use the checked JDK.

Before you run the class, predict which message the Console will show.

  • Select JavaFundamentalsDemo.java in the project tree.
  • Right-click JavaFundamentalsDemo.java.
  • Select Run As.
  • Select Java Application.

You should see Java workshop ready. in the Eclipse Console. That output proves your saved source file compiled successfully.

What does this prove?

  • The checked full JDK compiles JavaFundamentalsDemo.java.
  • The Console view displays the line written by System.out.println.

Do you see a different result?

  • Save JavaFundamentalsDemo.java before running it again.
  • Select JavaFundamentalsDemo.java instead of the project name.
  • Return to Installed JREs if Eclipse reports a missing Java environment.

Help me diagnose why the starter application does not print the expected message.

You have your first successful Java run. Next, you will expand this class with typed values, operators, and readable names.

Explore Java Types and Operators

Your starter message proves that Java compiles through Eclipse. It also proves that the Console can show your program's output.

Your future counter depends on typed state. This console example shows how operators turn stored values into visible results.

In this step, get ready to:
  • Apply readable naming conventions to constants and variables.
  • Declare all eight primitive types plus a String reference.
  • Print the stored values after evaluating Java expressions.
Define readable typed values

Java declarations pair readable names with fixed types. The declarations below cover all eight primitive types plus a String reference.

The = operator assigns each initial value. The >= operator compares bonusPoints with PASSING_SCORE to produce the boolean stored in passed.

  • Select all of the existing code in JavaFundamentalsDemo.java.
  • Replace the selected code by pasting the code below:
public class JavaFundamentalsDemo {
    private static final int PASSING_SCORE = 75;

    public static void main(String[] args) {
        byte labGroup = 1;
        short totalStudents = 40;
        int bonusPoints = 5;
        long studentId = 20260001L;
        float attendanceRate = 0.95F;
        double labAverage = 87.5;
        char section = 'A';
        boolean passed = bonusPoints >= PASSING_SCORE;
        String studentName = "Alex";

        System.out.println("Student: " + studentName);
        System.out.println("Passed: " + passed);
    }
}

How do these declarations work?

  • The PASSING_SCORE constant uses UPPER_SNAKE_CASE. Its static final modifiers keep the value fixed at 75.
  • The labGroup, totalStudents, bonusPoints, and studentId variables store whole numbers with different ranges.
  • The uppercase L identifies 20260001L as a long literal.
  • The attendanceRate variable uses float. The uppercase F identifies its decimal literal as a float.
  • The labAverage variable uses double for a decimal value.
  • The section variable uses char for one character inside single quotes.
  • The passed variable uses boolean to hold the result of the comparison.
  • The studentName variable refers to a String object containing text inside double quotes.
  • The + operator adds values when both operands are numeric. A String operand makes the same operator concatenate the label with its value.
  • Save JavaFundamentalsDemo.java.

Before you run the program, do you expect passed to display true or false?

  • Right-click the JavaFundamentalsDemo.java editor.
  • Select Run As.
  • Select Java Application.

That first typed run is working. The Console shows Student: Alex followed by Passed: false.

The comparison produces false because 5 does not meet the 75 threshold.

Seeing a compilation problem?

Check that PASSING_SCORE sits inside the class above main. Confirm that every variable declaration remains inside main.

Match every capital letter in the code. Java treats String differently from a lowercase spelling.

Use the first red underline to find the earliest problem. Later warnings often disappear after that first issue is fixed.

Help me diagnose my Java variable declarations in Eclipse.

Print every stored value

Console output turns stored program state into evidence you can inspect. A descriptive label connects each printed value to its source variable.

  • Place your cursor on the blank line below the statement that prints the passed value.
  • Add the remaining output statements by pasting the code below:
        System.out.println("Student ID: " + studentId);
        System.out.println("Section: " + section);
        System.out.println("Lab group: " + labGroup);
        System.out.println("Total students: " + totalStudents);
        System.out.println("Bonus points: " + bonusPoints);
        System.out.println("Attendance rate: " + attendanceRate);
        System.out.println("Lab average: " + labAverage);

What do these statements prove?

Each System.out.println call sends one labeled value to the Console. This gives every demonstrated type a visible result.

The labels use String concatenation to keep the output readable. The variable names remain responsible for holding the actual data.

  • Save JavaFundamentalsDemo.java.

Before the final run, which output values do you expect to include decimal points?

  • Right-click the JavaFundamentalsDemo.java editor.
  • Select Run As.
  • Select Java Application.

What should you see?

Your final run is working when the Console displays these nine lines without compilation problems.

  • Student: Alex.
  • Passed: false.
  • Student ID: 20260001.
  • Section: A.
  • Lab group: 1.
  • Total students: 40.
  • Bonus points: 5.
  • Attendance rate: 0.95.
  • Lab average: 87.5.

Missing a value in the Console?

  • Confirm that every print statement sits inside the closing brace of main.
  • Match each printed variable name to its declaration above.
  • Fix the first red underline before rerunning the class.

Help me find a missing Java Console value.

✔️ Awesome, I've got everything!

Your console program now demonstrates all eight primitive types. It also shows a String reference plus assignment, comparison, addition, and concatenation behavior.

ⓧ I'd like to double check the full code

public class JavaFundamentalsDemo {
    private static final int PASSING_SCORE = 75;

    public static void main(String[] args) {
        byte labGroup = 1;
        short totalStudents = 40;
        int bonusPoints = 5;
        long studentId = 20260001L;
        float attendanceRate = 0.95F;
        double labAverage = 87.5;
        char section = 'A';
        boolean passed = bonusPoints >= PASSING_SCORE;
        String studentName = "Alex";

        System.out.println("Student: " + studentName);
        System.out.println("Passed: " + passed);
        System.out.println("Student ID: " + studentId);
        System.out.println("Section: " + section);
        System.out.println("Lab group: " + labGroup);
        System.out.println("Total students: " + totalStudents);
        System.out.println("Bonus points: " + bonusPoints);
        System.out.println("Attendance rate: " + attendanceRate);
        System.out.println("Lab average: " + labAverage);
    }
}

Your data now has clear names, fixed types, and visible results. Next, you will move state into fields before organizing behavior with methods.

Organize Code with Methods and Objects

Your console program now shows every primitive type through real output. Next, you will reorganize JavaFundamentalsDemo.java so each part has a focused job.

The current main method holds the program's state plus its behavior. That concentration makes the class harder to maintain as new features arrive.

In this step, get ready to:
  • Move lasting student data out of main.
  • Extract the score calculation plus summary output into focused code.
  • Connect the new behavior to main through a created instance.
Move selected values into fields

A field stores state on a particular class instance. The student name plus quiz score belong to that lasting state.

  • In Eclipse, return to the JavaFundamentalsDemo.java editor tab.
  • Find the PASSING_SCORE declaration near the top of the file.
  • Place the cursor on the blank line directly below the constant.
  • Place the instance declarations below PASSING_SCORE by pasting this code:
    private String studentName = "Alex";
    private int quizScore = 80;

What do these fields represent?

  • studentName stores the name used by the summary output.
  • quizScore stores the score before the bonus is applied.
  • private keeps the state inside the class implementation.
  • Save JavaFundamentalsDemo.java.
  • Right-click JavaFundamentalsDemo.java in the Package Explorer.
  • Select Run As.
  • Select Java Application.

The Console repeats the existing output without compilation errors. You will still see Student: Alex plus Bonus points: 5.

Seeing a compilation problem?

Check that both field declarations sit below PASSING_SCORE but above main. A declaration placed inside main becomes a local variable.

Help me check the placement of my Java instance fields.

Extract focused methods

A method gives one behavior a name. A parameter supplies the value that behavior needs.

The two closing braces at the bottom can look identical at first. One closes main while the final brace closes the entire class.

  • Scroll to the bottom of JavaFundamentalsDemo.java.
  • Find the closing brace that ends main.
  • Place the cursor after that brace.
  • Place the new behavior before the class's final brace by pasting this code:
    private int addBonus(int bonusPoints) {
        return quizScore + bonusPoints;
    }

    private void printSummary(int finalScore, boolean passed) {
        System.out.println("Student: " + studentName);
        System.out.println("Final score: " + finalScore);
        System.out.println("Passed: " + passed);
    }

What do these methods do?

  • addBonus(int bonusPoints) receives the bonus through a parameter.
  • return quizScore + bonusPoints sends the calculated integer back to the caller.
  • printSummary(int finalScore, boolean passed) receives the finished score plus its comparison result.
  • void declares that printSummary() does not return a value.
  • Save JavaFundamentalsDemo.java.
  • Right-click JavaFundamentalsDemo.java in the Package Explorer.
  • Select Run As.
  • Select Java Application.

The Console again prints the existing values without compilation errors. This successful run confirms that both method declarations sit inside the class.

Are the new methods underlined?

Check that addBonus() plus printSummary() sit outside main. Both methods must remain inside the final class brace.

Help me fix the placement of my Java methods.

An object is a created instance of a class. The new expression creates the instance that owns the fields plus runs the methods.

  • Inside main, find the section declaration.
  • Select every line beneath it through the line that prints labAverage.
  • Replace the selected lines by pasting this object-driven flow:

        JavaFundamentalsDemo demo = new JavaFundamentalsDemo();
        int finalScore = demo.addBonus(bonusPoints);
        boolean passed = finalScore >= PASSING_SCORE;

        demo.printSummary(finalScore, passed);
        System.out.println("Student ID: " + studentId);
        System.out.println("Section: " + section);
        System.out.println("Lab group: " + labGroup);
        System.out.println("Total students: " + totalStudents);
        System.out.println("Attendance rate: " + attendanceRate);
        System.out.println("Lab average: " + labAverage);

How does the new flow work?

  • new JavaFundamentalsDemo() creates the instance.
  • demo stores a reference to that instance.
  • demo.addBonus(bonusPoints) calculates the final score from the field plus the local bonus.
  • finalScore >= PASSING_SCORE produces the boolean value stored in passed.
  • demo.printSummary(finalScore, passed) sends both results to the summary method.

Before you run the reorganized program, predict whether the bonus will affect passed before or after the comparison.

  • Save JavaFundamentalsDemo.java.
  • Right-click JavaFundamentalsDemo.java in the Package Explorer.
  • Select Run As.
  • Select Java Application.
Student: Alex
Final score: 85
Passed: true
Student ID: 20260001
Section: A
Lab group: 1
Total students: 40
Attendance rate: 0.95
Lab average: 87.5

What should you see?

The summary now appears first with a final score of 85. The comparison uses that final score before reporting Passed: true.

That is the refactor working. Your instance now owns the student state while main coordinates the program flow.

Does the output differ?

Check that finalScore comes from demo.addBonus(bonusPoints). Confirm that the comparison uses finalScore instead of bonusPoints.

Help me trace an incorrect score or pass result.

Trace the object flow and diagnose errors

The output proves that the refactor runs. Tracing the call path gives you a repeatable way to locate mistakes in larger programs.

  • Locate the demo object creation inside main.
  • Trace demo.addBonus(bonusPoints) to the addBonus() method.
  • Trace demo.printSummary(finalScore, passed) to the printSummary() method.

Before the final run, predict the order in which the summary lines plus the remaining type examples will reach the Console.

  • Right-click JavaFundamentalsDemo.java in the Package Explorer.
  • Select Run As.
  • Select Java Application.

You will see Student: Alex, Final score: 85, plus Passed: true before the remaining primitive-type output.

Use the first reported problem

If Eclipse shows red underlines, select the Problems view. Start with the first reported issue because later issues may come from the same misplaced brace or misspelled name.

Help me follow a repeatable Eclipse debugging routine.

✔️ Awesome, I've got everything!

Your class now separates instance state from calculation plus output behavior. Save the file before moving to the interface.

ⓧ I'd like to double check the full code

public class JavaFundamentalsDemo {
    private static final int PASSING_SCORE = 75;

    private String studentName = "Alex";
    private int quizScore = 80;

    public static void main(String[] args) {
        byte labGroup = 1;
        short totalStudents = 40;
        int bonusPoints = 5;
        long studentId = 20260001L;
        float attendanceRate = 0.95F;
        double labAverage = 87.5;
        char section = 'A';

        JavaFundamentalsDemo demo = new JavaFundamentalsDemo();
        int finalScore = demo.addBonus(bonusPoints);
        boolean passed = finalScore >= PASSING_SCORE;

        demo.printSummary(finalScore, passed);
        System.out.println("Student ID: " + studentId);
        System.out.println("Section: " + section);
        System.out.println("Lab group: " + labGroup);
        System.out.println("Total students: " + totalStudents);
        System.out.println("Attendance rate: " + attendanceRate);
        System.out.println("Lab average: " + labAverage);
    }

    private int addBonus(int bonusPoints) {
        return quizScore + bonusPoints;
    }

    private void printSummary(int finalScore, boolean passed) {
        System.out.println("Student: " + studentName);
        System.out.println("Final score: " + finalScore);
        System.out.println("Passed: " + passed);
    }
}

How to compare the file

Compare the field placement first. Next, compare the flow inside main plus the two methods below it.

Your class now keeps state in fields plus behavior in named methods. Next, you will use the same structure to build a visible click counter interface.

Build the Click Counter Interface

Your console program now keeps state in fields. Its focused methods organize behavior.

The next stage gives those ideas a visible home. Swing turns Java objects into a desktop interface.

In this step, get ready to:
  • Create a separate class for the desktop app.
  • Build the counter display with its two controls.
  • Test the first version of the interface.
Create the GUI class

A public Java class needs a source file with matching spelling and capitalization. The name ClickCounterApp connects the new class to ClickCounterApp.java.

  • In Eclipse, select the source folder under JavaFundamentalsWorkshop.
  • Right-click the selected source folder.
  • Select New from the context menu.
  • Select Class.
  • Enter ClickCounterApp as the class name.
  • Leave the package field blank.
  • Select the public static void main(String [] args) method stub.
  • Click Finish.

Good progress. ClickCounterApp.java now gives the desktop app its own source file.

Build the visible components

The interface uses JFrame for the window. It uses JLabel for the displayed state.

Two JButton objects provide the controls. FlowLayout arranges every component in one centered row.

  • Place your cursor above the public class ClickCounterApp declaration.
  • Add the required imports by pasting this code:
import java.awt.FlowLayout;
import javax.swing.*;

What do these imports provide?

  • The java.awt.FlowLayout import makes the layout class available by its short name.
  • The javax.swing.* import provides the Swing classes used by this interface.
  • Save ClickCounterApp.java.
  • Confirm the Problems view shows no import errors.

Seeing an import error?

Confirm both import statements sit above the public class declaration. Check that FlowLayout uses the same capitalization in the import.

Help me fix the imports in ClickCounterApp.java without changing the required class names.

Swing schedules interface startup on its event-dispatch thread. SwingUtilities.invokeLater places the window creation work on that thread.

  • Select everything from public class ClickCounterApp { through the class's final closing brace.
  • Replace the selected class by pasting this complete class body:
public class ClickCounterApp {
    private static final String WINDOW_TITLE = "Click Counter";

    private int count = 0;
    private final JLabel countLabel = new JLabel("Count: 0");

    private void createAndShowGui() {
        JFrame frame = new JFrame(WINDOW_TITLE);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout(FlowLayout.CENTER, 12, 20));

        JButton incrementButton = new JButton("Increment");
        JButton resetButton = new JButton("Reset");

        frame.add(countLabel);
        frame.add(incrementButton);
        frame.add(resetButton);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ClickCounterApp app = new ClickCounterApp();
            app.createAndShowGui();
        });
    }
}

What does this class build?

  • The WINDOW_TITLE constant stores the reusable window title.
  • The count field stores the counter's integer state.
  • The countLabel field stores the reusable display object.
  • The createAndShowGui() method creates the window and its controls.
  • The centered FlowLayout adds horizontal and vertical gaps around the components.
  • The pack() call sizes the window around its components.
  • The setVisible(true) call shows the completed window.
  • Save ClickCounterApp.java.
  • Confirm the Problems view shows no Java errors.

Seeing Java errors?

Confirm the two imports remain above the class declaration. Check that every opening brace has a matching closing brace.

Help me find the first compilation error in ClickCounterApp.java.

Run and test the first interface

Before you run the app, do you think each control already knows how to change the displayed count?

  • Select ClickCounterApp.java in the project tree.
  • Right-click the selected file.
  • Select Run As.
  • Select Java Application.

Your first Swing window is live. You will see Count: 0 beside Increment and Reset.

  • Click Increment.
  • Click Reset.

The label remains Count: 0 after each click. This intended shortfall proves that visible buttons still need behavior.

Window not appearing?

Confirm that you ran ClickCounterApp.java instead of JavaFundamentalsDemo.java. Check that main calls SwingUtilities.invokeLater.

Help me diagnose why the Click Counter window does not appear.

✔️ Awesome, I've got everything!

Your window displays the counter and both controls. The inactive-button test matches this first interface version.

ⓧ I'd like to double check the full code

import java.awt.FlowLayout;
import javax.swing.*;

public class ClickCounterApp {
    private static final String WINDOW_TITLE = "Click Counter";

    private int count = 0;
    private final JLabel countLabel = new JLabel("Count: 0");

    private void createAndShowGui() {
        JFrame frame = new JFrame(WINDOW_TITLE);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout(FlowLayout.CENTER, 12, 20));

        JButton incrementButton = new JButton("Increment");
        JButton resetButton = new JButton("Reset");

        frame.add(countLabel);
        frame.add(incrementButton);
        frame.add(resetButton);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ClickCounterApp app = new ClickCounterApp();
            app.createAndShowGui();
        });
    }
}

Your counter interface is visible and ready for interaction. Next, you will connect each click to a named method that updates the state and refreshes the label.

Connect the Counter Buttons

Your Swing window already displays the counter state with two visible buttons.

The buttons still lack a registered response. An event listener connects each click to named behavior that changes the state and refreshes the label.

In this step, get ready to:
  • Centralize the label refresh.
  • Implement the counter actions.
  • Connect the buttons to the counter actions.
Create one display refresh path

The number stored in count is the counter's state. A dedicated updateCountLabel() method keeps the visible label synchronized with that state.

  • In ClickCounterApp.java, find the closing brace of createAndShowGui().
  • Place your cursor before the main() method.
  • Create the shared display refresh method by pasting this code:
    private void updateCountLabel() {
        countLabel.setText("Count: " + count);
    }

What does this method do?

  • The setText() call replaces the label's displayed text.
  • The + operator joins Count: with the current integer value.
  • Every counter action can now reuse one display refresh path.
  • Save ClickCounterApp.java.
  • Check the source editor for red underlines around updateCountLabel().

You should see no compilation problem around the new method. Eclipse now recognizes the label refresh behavior.

Seeing a red underline?

Confirm that updateCountLabel() sits inside the ClickCounterApp class. Check that it remains outside createAndShowGui().

Compare the capitalization of countLabel with the field near the top of the class.

Help me fix the compilation problem in my updateCountLabel method.

Add the counter actions

Named methods keep each button responsible for one clear action. Both methods change count before they reuse the display refresh method.

  • In ClickCounterApp.java, find updateCountLabel().
  • Add the increment behavior immediately above updateCountLabel() by pasting this code:
    private void incrementCount() {
        count++;
        updateCountLabel();
    }

How does incrementing work?

  • The count++ statement increases the integer field by one.
  • The call to updateCountLabel() displays the new value after the state changes.
  • Save ClickCounterApp.java.
  • Check the Problems view at the bottom of Eclipse.

You should see no compilation error for incrementCount(). The increment action is ready to receive a click.

Is incrementCount underlined?

Confirm that count++ includes two plus signs. Check that the method closes before updateCountLabel() begins.

Help me fix my incrementCount method.

  • Place your cursor between incrementCount() and updateCountLabel().
  • Add the reset behavior by pasting this code:
    private void resetCount() {
        count = 0;
        updateCountLabel();
    }

How does resetting work?

  • The assignment count = 0 restores the integer field to its starting value.
  • The call to updateCountLabel() immediately displays that value.
  • Incrementing and resetting now share the same label update logic.
  • Save ClickCounterApp.java.
  • Check the Problems view for errors around resetCount().

You should see no compilation errors around either counter method. Both actions now update the field before refreshing the display.

Is resetCount causing an error?

Confirm that the assignment uses lowercase count. Check that the statement ends with a semicolon.

Help me fix my resetCount method.

Connect the buttons

The action methods need listeners before button clicks can reach them. Each short lambda accepts the click event through the event parameter and calls one named method.

  • In createAndShowGui(), find the line that adds resetButton to the frame.
  • Add both listener registrations immediately below that line by pasting this code:
        incrementButton.addActionListener(event -> incrementCount());
        resetButton.addActionListener(event -> resetCount());

How does a click reach the label?

  • Each addActionListener() call registers a response on one button.
  • The event parameter represents the button action that triggered the listener.
  • The Increment listener calls incrementCount().
  • The Reset listener calls resetCount().
  • Save ClickCounterApp.java.

Before you run the app, what number do you expect after one Increment click?

  • Select ClickCounterApp.java in Eclipse.
  • Right-click ClickCounterApp.java.
  • Select Run As.
  • Select Java Application.
  • Click Increment once.

You should see Count: 1. That visible change proves the click reached the listener and refreshed the label.

Does the button still do nothing?

Confirm that both listener lines remain inside createAndShowGui() before frame.pack().

Check that the Increment listener calls incrementCount(). Check that the Reset listener calls resetCount().

Help me trace why my Swing button listener is not updating the counter.

  • Close the running Click Counter window.

Before the deliberate test, what values should three Increment clicks and one Reset click display?

  • Right-click ClickCounterApp.java.
  • Select Run As.
  • Select Java Application.
  • Click Increment three times.

You should see Count: 3 in the window.

  • Click Reset.

You should see Count: 0. The visible label now matches the reset integer state.

  • Close the Click Counter window.

Before you reopen the app, what starting value do you expect a new ClickCounterApp object to display?

  • Right-click ClickCounterApp.java.
  • Select Run As.
  • Select Java Application.

You should see Count: 0 when the new window opens. A new app object starts with the field value declared in the class.

✔️ Awesome, I've got everything!

Your Increment and Reset buttons now update the counter through named methods. Double-check that you saved ClickCounterApp.java.

ⓧ I'd like to double check the full code

import java.awt.FlowLayout;
import javax.swing.*;

public class ClickCounterApp {
    private static final String WINDOW_TITLE = "Click Counter";

    private int count = 0;
    private final JLabel countLabel = new JLabel("Count: 0");

    private void createAndShowGui() {
        JFrame frame = new JFrame(WINDOW_TITLE);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout(FlowLayout.CENTER, 12, 20));

        JButton incrementButton = new JButton("Increment");
        JButton resetButton = new JButton("Reset");

        frame.add(countLabel);
        frame.add(incrementButton);
        frame.add(resetButton);

        incrementButton.addActionListener(event -> incrementCount());
        resetButton.addActionListener(event -> resetCount());

        frame.pack();
        frame.setVisible(true);
    }

    private void incrementCount() {
        count++;
        updateCountLabel();
    }

    private void resetCount() {
        count = 0;
        updateCountLabel();
    }

    private void updateCountLabel() {
        countLabel.setText("Count: " + count);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ClickCounterApp app = new ClickCounterApp();
            app.createAndShowGui();
        });
    }
}

You have completed the interaction loop. Each click now moves through a listener, a named method, the integer state, and the refreshed label.

Secret mission

Add a Safe Decrement Button

Extend the counter with a Decrement button that moves the value down without crossing zero. Use a conditional boundary to keep the displayed count valid.

Clean Up Your Resources

Clean Up Your Resources

Choose whether to keep your local project available, pause your work for later, or delete the project entirely. This workshop runs on your Windows computer, so there are no cloud resources or ongoing service costs.

Resources you used:

  • A local Eclipse project named JavaFundamentalsWorkshop.
  • The Java source file JavaFundamentalsDemo.java.
  • The Java source file ClickCounterApp.java.

Keep everything running

No action is needed. Choose this if you want to keep practising with the same project.

  • Retain JavaFundamentalsWorkshop in your Eclipse workspace.
  • Run JavaFundamentalsDemo.java whenever you want to review the console examples.
  • Run ClickCounterApp.java whenever you want to practise Swing event handling.

Pause - I'll come back to this later

Shut down the desktop tools while preserving your project files. Choose this if you plan to return later.

  • Close the Click Counter window if it is still running.
  • Close Eclipse.
  • Leave JavaFundamentalsWorkshop unchanged in your Eclipse workspace.

Delete - I don't want to use this again

Deleting the project removes both Java source files when you include the files stored on your computer. Your Eclipse installation and JDK configuration remain available for future projects.

  • Select JavaFundamentalsWorkshop from the project list in Eclipse.
  • Use Eclipse's project deletion action for JavaFundamentalsWorkshop.
  • Choose the option that also removes the project files from your computer.
  • Confirm the deletion.
  • Verify that JavaFundamentalsWorkshop no longer appears in Eclipse.

Nice Work!

Nice Work!

You did it! Your Java console program now leads into a Swing click counter with working Increment, Reset, and Decrement controls.

What you learned:

  • Used typed values and operators in JavaFundamentalsDemo.java to print every primitive type plus a String reference.
  • Modeled object state with fields. Organized behavior with focused methods, parameters, and return values.
  • Connected Swing events to incrementCount() and resetCount(). Reused updateCountLabel() to keep the visible label aligned with count.
  • Secret Mission: Added a safe Decrement action. Used if (count > 0) inside decrementCount() to prevent negative counts.

Ready to quiz yourself?