skip to content

Observer in Java

Observer in the JDK spans Swing and property-change listeners and the reactive Flow.Publisher/Flow.Subscriber API, while the legacy java.util.Observable is deprecated. Interviewers ask why the old API was abandoned and what replaced it.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

Why were java.util.Observable and java.util.Observer deprecated in Java 9, and what should you use instead?

level: juniorimportance: should knowfreq 55%

answer

  1. Observable is a class -> single-inheritance trap
  2. Object arg = untyped events, casts everywhere
  3. setChanged() ceremony before notifyObservers()
  4. Deprecated Java 9, not removed
  5. Replace with PropertyChangeSupport or Flow

basics

~20 s

The old Observable/Observer classes were deprecated in Java 9 because they were weak and limited: Observable is a class (so you must extend it), it isn't thread-safe in a useful way, and it passes plain Object events. Use listeners, PropertyChangeSupport, or the reactive Flow API instead.

solid answer

~40 s

java.util.Observable (a class) and java.util.Observer (an interface) were the original Observer-pattern support in Java 1.0, deprecated in Java 9. They have real flaws: Observable is a concrete class, so a subject must extend it, blocking other inheritance; events are untyped (Object), forcing casts; change tracking via setChanged()/notifyObservers() is clumsy and easy to misuse; ordering and concurrency guarantees are weak; and it isn't Serializable-friendly. Modern replacements depend on the need: java.beans.PropertyChangeSupport/PropertyChangeListener for typed property-change notification, framework listener interfaces (e.g. Swing ActionListener), or java.util.concurrent.Flow (the reactive-streams Publisher/Subscriber API) for asynchronous backpressure-aware streams. The library still compiles for backward compatibility but you should not build new code on it.

go deeper

for a junior

Knows the legacy Observable/Observer were deprecated in Java 9 and that you should use listeners or Flow instead.

for a middle

Can name the concrete flaws (class not interface, untyped Object args, setChanged ceremony) and map each modern replacement to a scenario.

for a senior

Explains the inheritance/composition trade-off, the deprecation policy (compiles but discouraged), and picks PropertyChangeSupport vs Flow appropriately with reasoning about sync vs async and backpressure.

for a principal

Frames the deprecation as part of the JDK's shift toward composition, typed events, and the reactive-streams contract; can discuss migration strategy for legacy Observable-based code at scale.

## What the Observer pattern is The **Observer pattern** is a design pattern where one object (the **subject** or **publisher**) maintains a list of dependents (**observers** or **subscribers**) and notifies them automatically when its state changes. It decouples the thing producing events from the things reacting to them: the subject does not need to know the concrete types of its observers, only that they implement an agreed callback. ## The original JDK support: java.util.Observable / java.util.Observer Java 1.0 shipped two types to support this: - **`java.util.Observer`** — an interface with one method, `void update(Observable o, Object arg)`. An observer implements this to react. - **`java.util.Observable`** — a **concrete class** the subject must *extend*. It holds the observer list and offers `addObserver`, `deleteObserver`, `setChanged()`, and `notifyObservers(Object arg)`. The notification protocol is unusual: a subject must call `setChanged()` to flip an internal "changed" flag before `notifyObservers()` will actually fire; `notifyObservers()` then clears the flag. Forgetting `setChanged()` silently suppresses notifications. ## Why it was deprecated in Java 9 (JDK-8154801) It was deprecated (not removed) for several concrete reasons: 1. **`Observable` is a class, not an interface.** Java has single inheritance, so a subject that extends `Observable` cannot extend anything else. This is the classic "favor composition over inheritance" violation — you are forced into an inheritance relationship just to get notification support. 2. **Untyped events.** Both the `Observable o` and the `Object arg` passed to `update` are untyped. Observers must cast and there is no compile-time guarantee about what they receive. 3. **The `setChanged()` ceremony is error-prone.** The two-step changed-flag protocol is easy to get wrong and surprising. 4. **Weak concurrency and ordering guarantees.** `notifyObservers` invokes observers in an unspecified order; the threading model and what happens if the list changes during notification are not well defined for modern concurrent use. 5. **No serialization / event-model integration.** It predates and does not fit the JavaBeans event model or reactive streams. `@Deprecated` here means: still present and compiling for backward compatibility, but a clear signal not to use it in new code. ## What to use instead (chosen by your actual need) - **Typed property-change notification** — `java.beans.PropertyChangeSupport` (a helper you *compose*, not extend) plus `PropertyChangeListener`. You fire `firePropertyChange(name, oldValue, newValue)` and listeners get a typed `PropertyChangeEvent`. This is the idiomatic single-VM, synchronous replacement. - **Framework listeners** — most UI/IO frameworks define their own listener interfaces (e.g. Swing's `ActionListener`, `ChangeListener`). These are the Observer pattern, just typed and domain-specific. - **Reactive / asynchronous streams** — `java.util.concurrent.Flow` (added in Java 9) defines `Flow.Publisher`, `Flow.Subscriber`, `Flow.Subscription`, and `Flow.Processor`: the standard reactive-streams contract with **backpressure** (the subscriber requests how many items it can handle). Use this for async, possibly-infinite event streams. ## Deriving your own answer A junior should be able to say "the old classes were deprecated; use listeners or Flow." A senior adds *why* (class-not-interface, untyped, setChanged ceremony) and *which replacement for which scenario* (PropertyChangeSupport for sync property changes, Flow for async streams).

  • What is the single biggest design flaw of java.util.Observable being a class?
    Java has single inheritance, so a subject forced to extend Observable cannot extend any other class. It violates 'favor composition over inheritance' — composition-based helpers like PropertyChangeSupport avoid this by being a field you hold rather than a parent you extend.
  • If I just need typed property-change notifications inside one JVM, what is the idiomatic replacement?
    java.beans.PropertyChangeSupport composed into your class, firing PropertyChangeEvents to registered PropertyChangeListeners — synchronous, typed, and no inheritance requirement.

saying these in an interview costs you the question

  • Saying it was removed from the JDK (it was only deprecated, still compiles)
  • Claiming Observable is an interface (it is a concrete class)
  • Recommending you keep using Observable for new code
  • Forgetting that notifyObservers does nothing unless setChanged() was called first

context

open as a page

How does the JavaBeans PropertyChangeSupport / PropertyChangeListener mechanism implement the Observer pattern, and how do you use it correctly?

level: middleimportance: should knowfreq 50%

basics

~20 s

A bean keeps a PropertyChangeSupport helper object. Listeners register with addPropertyChangeListener. When a property changes, the bean calls firePropertyChange(name, oldValue, newValue), and every listener's propertyChange method runs with a PropertyChangeEvent. It only fires when old and new values actually differ.

open as a page

What is the java.util.concurrent.Flow API, and how do Flow.Publisher, Flow.Subscriber, and Flow.Subscription cooperate to deliver backpressure?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Flow (added in Java 9) is the JDK's reactive-streams API: a Publisher produces items, a Subscriber consumes them. When you subscribe, you get a Subscription and must call request(n) to ask for n items — the publisher only sends up to what you requested. That request mechanism is backpressure, so a fast producer can't overwhelm a slow consumer.

open as a page

How do Swing listeners (e.g. ActionListener) embody the Observer pattern, and why is the event-dispatch thread central to using them safely?

level: middleimportance: nice to knowfreq 35%

basics

~20 s

A Swing component (the subject) keeps a list of listeners. You register one with e.g. button.addActionListener(...). When the user acts, the component fires an event and calls each listener's callback. All this runs on the Event Dispatch Thread (EDT), so listener code must stay on that thread and not block it.

open as a page

When designing notification in modern Java, how do you choose between synchronous listeners (PropertyChangeListener/Swing), the reactive Flow API, and avoiding the deprecated Observable — and what trade-offs drive the choice?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Use synchronous listeners (PropertyChangeListener, Swing) for simple in-process events where the producer can safely call observers directly. Use the Flow reactive API for asynchronous streams of many items where a slow consumer needs backpressure. Never base new code on the deprecated java.util.Observable.

open as a page