# How to Implement the Proxy Pattern for Lazy Initialization in Java

> Implement the Proxy pattern for lazy initialization in Java. Delay expensive object creation until needed, conserving resources effectively.

- Repository: [Ilkka Seppälä/java-design-patterns](https://github.com/iluwatar/java-design-patterns)
- Tags: how-to-guide
- Published: 2026-02-27

---

**The Proxy pattern enables lazy initialization by interposing a surrogate object that instantiates the expensive real subject only when its methods are first invoked, minimizing resource usage until absolutely necessary.**

The **java-design-patterns** repository provides a complete reference implementation demonstrating how to defer costly object creation using the Virtual Proxy subtype. This approach ensures that heavy initialization logic—such as loading large media files or establishing database connections—executes only when the client actually requests the operation, not at application startup.

## Understanding the Virtual Proxy Pattern

A **Virtual Proxy** acts as a lightweight placeholder for an object that is expensive to create or resource-intensive to maintain. It implements the same interface as the real subject, allowing client code to remain agnostic about whether it holds a proxy or the actual object. The proxy intercepts method calls and performs lazy instantiation, forwarding all subsequent requests to the newly created real instance.

## Core Source Files in java-design-patterns

According to the source code in `iluwatar/java-design-patterns`, the Virtual Proxy implementation resides in the `virtual-proxy` module. The four critical files that compose this pattern are:

- **[`virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/ExpensiveObject.java`](https://github.com/iluwatar/java-design-patterns/blob/main/virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/ExpensiveObject.java)** — The service interface defining the contract for both the real object and its proxy.
- **[`virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/RealVideoObject.java`](https://github.com/iluwatar/java-design-patterns/blob/main/virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/RealVideoObject.java)** — The concrete implementation containing the expensive constructor logic.
- **[`virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/VideoObjectProxy.java`](https://github.com/iluwatar/java-design-patterns/blob/main/virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/VideoObjectProxy.java)** — The surrogate class that delays instantiation until the first method invocation.
- **[`virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/App.java`](https://github.com/iluwatar/java-design-patterns/blob/main/virtual-proxy/src/main/java/com/iluwatar/virtual/proxy/App.java)** — The client application that interacts exclusively with the interface.

## Step-by-Step Implementation

### 1. Define the Service Interface

Both the real subject and the proxy implement a common interface. This abstraction ensures the client depends only on the contract, not on concrete classes.

```java
public interface ExpensiveObject {
    void process();
}

```

### 2. Implement the Real Subject

The real subject encapsulates the heavy initialization work inside its constructor. In [`RealVideoObject.java`](https://github.com/iluwatar/java-design-patterns/blob/main/RealVideoObject.java), the `heavyInitialConfiguration()` method simulates this costly setup.

```java
@Slf4j
public class RealVideoObject implements ExpensiveObject {

    public RealVideoObject() {
        heavyInitialConfiguration();
    }

    private void heavyInitialConfiguration() {
        LOGGER.info("Loading initial video configurations...");
    }

    @Override
    public void process() {
        LOGGER.info("Processing and playing video content...");
    }
}

```

### 3. Build the Lazy-Loading Proxy

The `VideoObjectProxy` class holds a `null` reference to `RealVideoObject` until the client invokes `process()`. On the first call, it constructs the real object and caches the reference for future delegations.

```java
@Getter
public class VideoObjectProxy implements ExpensiveObject {

    private RealVideoObject realVideoObject;

    @Override
    public void process() {
        if (realVideoObject == null) {
            realVideoObject = new RealVideoObject();
        }
        realVideoObject.process();
    }
}

```

### 4. Wire the Client Application

The client instantiates the proxy rather than the real object. This configuration keeps the expensive constructor from running until the first `process()` call.

```java
public class App {
    public static void main(String[] args) {
        ExpensiveObject video = new VideoObjectProxy();
        video.process(); // Triggers RealVideoObject creation
        video.process(); // Uses cached instance
    }
}

```

## Thread-Safe Proxy Implementation

The basic implementation above is not thread-safe. For concurrent environments, apply **double-checked locking** to ensure only one thread initializes the real object. Mark the reference as `volatile` to prevent instruction reordering issues.

```java
public class ThreadSafeVideoObjectProxy implements ExpensiveObject {

    private volatile RealVideoObject realVideoObject;

    @Override
    public void process() {
        if (realVideoObject == null) {
            synchronized (this) {
                if (realVideoObject == null) {
                    realVideoObject = new RealVideoObject();
                }
            }
        }
        realVideoObject.process();
    }
}

```

## Key Benefits of This Approach

- **Performance Optimization**: Expensive resources allocate only when required, reducing application startup time and memory footprint.
- **Encapsulation**: Client code remains decoupled from object creation logic, depending solely on the `ExpensiveObject` interface.
- **Extensibility**: The proxy can transparently add cross-cutting concerns such as logging, access control, or result caching before delegating to the real subject.

## Summary

- The **Virtual Proxy** pattern defers instantiation of costly objects until their first use.
- Implementation requires a shared interface (`ExpensiveObject`), a real subject (`RealVideoObject`), and a proxy (`VideoObjectProxy`) that manages lazy creation.
- Source files are located in the `virtual-proxy` module of `iluwatar/java-design-patterns`.
- For multi-threaded scenarios, implement **double-checked locking** using a `volatile` reference to maintain thread safety without synchronizing every method call.

## Frequently Asked Questions

### What distinguishes a Virtual Proxy from a Decorator?

A **Virtual Proxy** controls access to an object primarily to delay its creation or manage resource-intensive instantiation, while a **Decorator** adds responsibilities or behaviors to an object dynamically without changing its interface. The proxy’s intent is lazy initialization; the decorator’s intent is functional enhancement.

### When should I use lazy initialization with a Proxy?

Use this pattern when an object is expensive to create (consumes significant memory, CPU, or I/O) and its use is infrequent or uncertain during the application lifecycle. Examples include loading large images, initializing database connections, or parsing massive configuration files.

### Is the standard Proxy implementation thread-safe by default?

No, the basic implementation in [`VideoObjectObject.java`](https://github.com/iluwatar/java-design-patterns/blob/main/VideoObjectObject.java) is not thread-safe. Without synchronization, multiple threads could simultaneously pass the `null` check and create multiple instances of the real subject. Use the **double-checked locking** pattern or initialize the proxy within a synchronized factory method for concurrent environments.

### How does the Virtual Proxy differ from a Remote Proxy or Protection Proxy?

The **Virtual Proxy** handles lazy initialization of expensive local objects. A **Remote Proxy** manages communication with objects residing in different address spaces (network transparency), while a **Protection Proxy** controls access permissions based on authentication levels. All three share the same structural interface but serve distinct functional purposes.