Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
215: An Overview of Swing for Legacy Applications
I've spent the last few mornings digging through a codebase for a client that hasn't been touched since 2008. It's a massive internal tool for managing server clusters, and it's built entirely on Swing. If you've only worked with modern web frameworks or JavaFX, Swing feels like stepping into a time machine. It's clunky, the default look is "Windows 95 grey," but it's incredibly stable. Since you'll likely run into this in any enterprise environment, let's walk through how it actually works by trying to recreate a piece of that server monitor.
Getting a Window on Screen
First, I wanted to see how easy it is to just get a basic window up. I'll start with a JFrame—that's the top-level container in Swing. I'll throw a button in there to simulate a "Connect to Server" action.
import javax.swing.*;
public class ServerMonitor {
public static void main(String[] args) {
JFrame frame = new JFrame("Legacy Server Monitor");
JButton connectBtn = new JButton("Connect to Server");
frame.add(connectBtn);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
When I ran this, I noticed something weird. The button didn't just sit in the middle; it stretched to fill the entire window. It looked absurd. I forgot that JFrame uses a BorderLayout by default. When you call add() without specifying a position, Swing just dumps the component into the "Center" region, and BorderLayout expands the center component to fill all available space.
Fighting with the Layout
I don't want one giant button. I want a label and a button side-by-side. To fix the stretching issue, I need a different layout manager. Most of us instinctively want to use absolute positioning (X, Y coordinates), but in Swing, that's a recipe for disaster because your UI will break the second someone resizes the window or opens it on a screen with a different DPI.
Instead, I'll use a JPanel with a FlowLayout. A JPanel is basically a generic container that I can stick inside the frame. I'll put the label and button inside the panel, and then put the panel inside the frame.
import javax.swing.*;
import java.awt.*;
public class ServerMonitor {
public static void main(String[] args) {
JFrame frame = new JFrame("Legacy Server Monitor");
// Create a panel with FlowLayout (default for JPanel)
JPanel panel = new JPanel();
JLabel statusLabel = new JLabel("Status: Disconnected");
JButton connectBtn = new JButton("Connect");
panel.add(statusLabel);
panel.add(connectBtn);
frame.add(panel); // Now the panel is in the Center, but components inside flow naturally
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
That's better. The components now respect their preferred size and wrap like text in a document. If I wanted the status label at the top and the buttons at the bottom, I'd probably use a combination of BorderLayout for the frame (putting a panel in NORTH and another in SOUTH). It's like building with LEGOs, but the blocks are sometimes stubborn.
The UI Freeze Mystery
Now, here is where most developers trip up with Swing. I wanted the "Connect" button to simulate a network call. I added an ActionListener that sleeps for two seconds to mimic a server response, then updates the label.
connectBtn.addActionListener(e -> {
try {
Thread.sleep(2000); // Simulate network lag
statusLabel.setText("Status: Connected!");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
When I clicked the button, the entire application froze. The window became unresponsive, I couldn't move it, and the label didn't update until after the two seconds were up. This happened because Swing is single-threaded. Everything—painting the window, handling clicks, updating text—happens on the Event Dispatch Thread (EDT). By calling Thread.sleep() inside the listener, I effectively told the UI thread to stop breathing for two seconds.
To fix this, I have to run the heavy lifting in a separate thread, but there's a catch: you are not allowed to update Swing components from any thread other than the EDT. If you do, you'll get random race conditions and weird rendering glitches. The solution is SwingUtilities.invokeLater(), which schedules a piece of code to run on the EDT as soon as it's free.
connectBtn.addActionListener(e -> {
// Start a new thread so we don't block the EDT
new Thread(() -> {
try {
Thread.sleep(2000);
// Now, jump back onto the EDT to update the UI
SwingUtilities.invokeLater(() -> {
statusLabel.setText("Status: Connected!");
});
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}).start();
});
Now the button clicks, the UI remains responsive (you can even drag the window around), and the label updates once the background work is finished. This pattern—Worker Thread $\rightarrow$ invokeLater $\rightarrow$ UI Update—is the heartbeat of every legacy Swing application you'll ever maintain.
📋 Practical Task
Build a Legacy Server Resource Monitor
Your goal is to create a small Swing application that mimics a server monitoring tool. The application must meet the following requirements:
- The Layout: Use a
JFramecontaining aJPanel. The panel should use aFlowLayoutto hold aJLabel(showing "CPU Usage: 0%") and aJButton(labeled "Refresh Stats"). - The Logic: When the "Refresh Stats" button is clicked, the application must simulate a data fetch.
- The "fetch" should happen on a background thread and last for 1.5 seconds.
- While the fetch is happening, the button should be disabled (
setEnabled(false)) so the user can't spam it. - After the 1.5 seconds, the
JLabelshould be updated to display a random CPU percentage (e.g., "CPU Usage: 42%") and the button should be re-enabled.
- The Constraint: All updates to the label and button must be wrapped in
SwingUtilities.invokeLater()to ensure thread safety on the Event Dispatch Thread.
There are no comments for now.