← Back to Advanced Java Course | Chapter 6: Design Patterns | Lesson 4 of 14

Java Observer Pattern

Subject and Observer Interfaces

The Observer Pattern defines a one-to-many dependency between objects: when one object, the Subject, changes state, every one of its registered dependents, the Observers, is notified automatically without the Subject needing to know anything specific about them.

Example: Subject and Observer Interfaces

java
public class Main {
	interface Observer { void update(String data); }
	interface Subject {
		void attach(Observer o);
		void notifyObservers(String data);
	}
	public static void main(String[] args) {
		System.out.println("Subject notifies every registered Observer automatically");
	}
}

Concrete Subject Implementation

A Concrete Subject tracks its observers in an internal list, exposing methods to attach or detach observers dynamically. When its state changes, it iterates through that list and calls each observer's update method to notify them.

Example: Concrete Subject Implementation

java
import java.util.*;
public class Main {
	interface Observer { void update(String data); }
	static class NewsPublisher {
		List<Observer> observers = new ArrayList<>();
		void attach(Observer o) { observers.add(o); }
		void publish(String data) {
			for (Observer o : observers) o.update(data);
		}
	}
	public static void main(String[] args) {
		NewsPublisher publisher = new NewsPublisher();
		publisher.attach(data -> System.out.println("Received: " + data));
		publisher.publish("Breaking news");
	}
}

Concrete Observer Implementation

A Concrete Observer implements the update method that the Subject calls into. Inside that method, it runs whatever custom logic is appropriate given the new data it just received, entirely independent of how the other observers respond to the same notification.

Example: Concrete Observer Implementation

java
public class Main {
	interface Observer { void update(String data); }
	static class LoggingObserver implements Observer {
		public void update(String data) {
			System.out.println("Logged: " + data); // independent of other observers
		}
	}
	public static void main(String[] args) {
		Observer observer = new LoggingObserver();
		observer.update("event occurred");
	}
}

Dynamic Subscriptions

Observers can subscribe or unsubscribe from a Subject dynamically at runtime, not just at setup. This flexibility prevents memory leaks from observers that should have been removed, and ensures each observer only ever receives the specific events it's actually interested in.

Example: Dynamic Subscriptions

java
import java.util.*;
public class Main {
	interface Observer { void update(String data); }
	static class Subject {
		List<Observer> observers = new ArrayList<>();
		void attach(Observer o) { observers.add(o); }
		void detach(Observer o) { observers.remove(o); }
		void notifyEach(String data) { for (Observer o : observers) o.update(data); }
	}
	public static void main(String[] args) {
		Subject subject = new Subject();
		Observer o = data -> System.out.println("Got: " + data);
		subject.attach(o);
		subject.detach(o); // unsubscribed before the next notification
		subject.notifyEach("no listeners left");
	}
}

Java Built-in Alternatives

Modern Java code commonly implements this pattern with custom, purpose-built listener interfaces rather than reaching for the JDK's original built-in Observer/Observable classes, which have been deprecated since Java 9 due to design limitations like lacking proper event ordering guarantees.

Example: Java Built-in Alternatives

java
public class Main {
	interface ChangeListener { void onChange(String value); } // custom listener, not java.util.Observable (deprecated since Java 9)
	public static void main(String[] args) {
		ChangeListener listener = value -> System.out.println("Changed to: " + value);
		listener.onChange("new state");
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.