Virtual Proxy
The Virtual Proxy acts as a representative for an object that may be expensive to create. The Virtual Proxy often defers the creation of the object until it is needed; the Virtual Proxy also acts as a surrogate for the object before and while it is being created. After that, the proxy delegates requests directly to the RealSubject.
Designing Virtual Proxy Example

Implementation
Creating ImageProxy:
import java.net.*;
import java.awt.*;
import javax.swing.*;
class ImageProxy implements Icon {
/* The imageIcon is the REAL icon that we eventually want to display when loaded. */
volatile ImageIcon imageIcon;
final URL imageURL;
Thread retrievalThread;
boolean retrieving = false;
/* We pass the URL of the image. This is the image to display when loaded */
public ImageProxy(URL url) { imageURL = url; }
/* We return a default width and height until the image is loaded */
public int getIconWidth() {
if (imageIcon != null) {
return imageIcon.getIconWidth();
} else {
return 800;
}
}
public int getIconHeight() {
if (imageIcon != null) {
return imageIcon.getIconHeight();
} else {
return 600;
}
}
/* Used by two different threads. With volatile (to protect reads), we use a synchronized setter (to protect writes) */
synchronized void setImageIcon(ImageIcon imageIcon) {
this.imageIcon = imageIcon;
}
public void paintIcon(final Component c, Graphics g, int x, int y) {
if (imageIcon != null) {
/* If we've got an icon, tell it to paint itself */
imageIcon.paintIcon(c, g, x, y);
} else {
/* Otherwise, we display the loading message */
g.drawString("Loading album cover, please wait...", x+300, y+190);
/* If we aren't already trying to load */
if (!retrieving) {
/* Only one thread calls paint, so it is safe. */
retrieving = true;
/* Use another thread to retrieve the image. */
retrievalThread = new Thread(new Runnable() {
public void run() {
try {
/* In thread, we instantiate the Icon object. Its constructor will not return until the image is loaded*/
setImageIcon(new ImageIcon(imageURL, "Album Cover"));
/* Repaint once image is loaded. */
c.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});
retrievalThread.start();
}
}
}
}