Unit 4 · Lesson 215 minAcademic review pending

A program that waits, instead of a program that runs top to bottom

Every program you have written so far ran your steps, in your order, start to finish. A GUI program runs a fixed setup, then waits — and your code only runs again when the user does something.

Choose explanation

After this lesson

You should be able to

  • Explain what an event source and an event listener are, in relation to each other.
  • Register a listener on a component and explain when Java calls it.
  • Contrast this event-driven flow with the top-to-bottom flow of every CS105ES program.
01

Inverting control: the program waits for you

Every C program from CS105ES followed main from the first line to the last, in an order the programmer fully controlled. A GUI program flips that: main sets up the window and its buttons, then hands control to Java's event loop, which sits idle until the user clicks, types, or moves the mouse. Your code runs in short bursts, triggered by the user, not in one continuous top-to-bottom pass.

This inversion is called event-driven programming, and it is the reason GUI code looks structurally different from anything written so far in this course — not because the language changed, but because the entire shape of when your code runs has changed.

02

Source, event object, and listener

An event source is a component that can generate events — a JButton, for instance. When the user clicks it, Java constructs an event object carrying details about what happened, and looks for a listener registered on that source. A listener is an object implementing a specific listener interface, with a method Java calls automatically the moment the matching event occurs.

This is why registering a listener with addActionListener is called delegation: the button itself contains no application logic at all, it simply delegates "something happened here" to whichever listener object you registered, keeping the component reusable and your logic separate from it.

The button delegates "clicked" to your code
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JButton;

JButton greetButton = new JButton("Greet");

greetButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button was clicked!");
    }
});

Try it yourself

Create two buttons, "Increment" and "Reset". Register a listener on each that updates and prints a counter variable — Increment adds one, Reset sets it back to zero. Note in one sentence why the counter variable cannot be a plain local variable inside main.

Need a hint?

A listener's method runs long after main has already finished setting up the window — any variable it needs must still be reachable at that later moment.

Check the worked solution

The counter must be a field of an enclosing class (or effectively final in this simplified form), not a plain local variable, because the listener's actionPerformed runs much later than main's own code — long after any ordinary local variable inside main would have already gone out of scope, exactly the scope-and-lifetime reasoning from CS105ES applied to a genuinely new timing situation.

class CounterDemo {
    private int count = 0;   // a field, still reachable when listeners fire later

    void setup() {
        JButton incrementButton = new JButton("Increment");
        JButton resetButton = new JButton("Reset");

        incrementButton.addActionListener(e -> {
            count++;
            System.out.println(count);
        });

        resetButton.addActionListener(e -> {
            count = 0;
            System.out.println(count);
        });
    }
}

Quick check

Why does registering greetButton.addActionListener(...) not immediately run the code inside actionPerformed?

Select an answer to check your thinking.

Why this lesson exists

Syllabus mapping

Event Handling: Events, Event sources, Event classes, Event Listeners, Delegation event model.

Maps to course outcome CO4.