Of course! The Java Look and Feel (L&F) is a fundamental concept in Swing development. It allows you to control the visual appearance (the "look") and the interactive behavior (the "feel") of your Swing GUI components.

Let's break it down comprehensively.
What is a Look and Feel?
In simple terms, a Look and Feel is a pre-packaged set of resources and logic that defines:
- Look: The colors, fonts, borders, and icons used to paint components like buttons, text fields, and menus.
- Feel: How components respond to user interaction. For example, how a button highlights when the mouse is over it, or how a checkbox behaves when clicked.
This separation allows you to create a consistent user experience across different platforms or to match the look of a specific operating system (like Windows or macOS).
The Default Look and Feel
By default, a Swing application uses a cross-platform Look and Feel called Nimbus (in Java 6 update 10 and later). Before Nimbus, the default was Metal.

You can easily check the current L&F in your application:
import javax.swing.*;
import java.awt.*;
public class CheckLAF {
public static void main(String[] args) {
// Run on the Event Dispatch Thread (EDT)
SwingUtilities.invokeLater(() -> {
// Get the currently installed Look and Feel
LookAndFeel laf = UIManager.getLookAndFeel();
System.out.println("Current Look and Feel: " + laf.getName());
System.out.println("Current Look and Feel Class: " + laf.getClass().getName());
});
}
}
Output on most modern systems:
Current Look and Feel: Nimbus
Current Look and Feel Class: javax.swing.plaf.nimbus.NimbusLookAndFeel
Built-in Look and Feels
Java comes with several built-in L&Fs out of the box.
| Look and Feel | Class Name | Description |
|---|---|---|
| Metal | javax.swing.plaf.metal.MetalLookAndFeel |
The original cross-platform L&F. It's highly customizable. |
| Nimbus | javax.swing.plaf.nimbus.NimbusLookAndFeel |
The modern default. It's more polished and easier to customize than Metal. |
| CDE/Motif | com.sun.java.swing.plaf.motif.MotifLookAndFeel |
An older, Unix-style L&F. Rarely used today. |
| Windows | com.sun.java.swing.plaf.windows.WindowsLookAndFeel |
Mimics the native Windows look and feel. |
| Windows Classic | com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel |
Mimics the older Windows 98/2000 style. |
| macOS | com.sun.java.swing.plaf.mac.MacLookAndFeel |
Mimics the native macOS look and feel. Note: This is only available on macOS systems. |
How to Change the Look and Feel
You should always set the L&F before creating any Swing components. The best place to do this is at the very beginning of your main method, inside the SwingUtilities.invokeLater block.

Example: Setting a Specific L&F
Here's how to force your application to use the Windows L&F.
import javax.swing.*;
import java.awt.*;
public class SetWindowsLAF {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
// Set the Look and Feel to Windows
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
// If the L&F is not available, fall back to the default
e.printStackTrace();
}
// Now, create and show the GUI
JFrame frame = new JFrame("Windows L&F Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
JButton button = new JButton("I look like a Windows button!");
frame.add(button, BorderLayout.CENTER);
frame.setVisible(true);
});
}
}
Example: Using the System's Native L&F
The most common and recommended approach is to use the Look and Feel of the operating system the application is running on. This provides the most native experience.
import javax.swing.*;
import java.awt.*;
public class SetNativeLAF {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
// Use the system's native Look and Feel
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Now, create and show the GUI
JFrame frame = new JFrame("Native L&F Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
JButton button = new JButton("I look like a native button!");
frame.add(button, BorderLayout.CENTER);
frame.setVisible(true);
});
}
}
Handling Exceptions
When you call UIManager.setLookAndFeel(), you must handle three types of exceptions, which are all subclasses of Exception:
ClassNotFoundException: The class name for the L&F you specified doesn't exist in the classpath. This can happen if you try to use a platform-specific L&F (like Windows) on a different OS (like Linux).InstantiationException: The L&F class cannot be instantiated. This is rare.IllegalAccessException: The L&F constructor is not accessible. This is also rare.UnsupportedLookAndFeelException: The L&F is installed but is not supported by the current runtime environment. This can happen with the macOS L&F if you're not running on a Mac.
A try-catch block is the standard way to handle this.
Customizing the Look and Feel (Theming)
While you can't easily create a completely new L&F from scratch, you can heavily customize the built-in ones, especially Metal and Nimbus.
Customizing Nimbus
Nimbus is designed to be customizable. You can change almost any color, font, or gradient by modifying the UIDefaults table.
Example: Changing Button Colors in Nimbus
import javax.swing.*;
import java.awt.*;
public class CustomizeNimbus {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
// Set the L&F to Nimbus first
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
// Now, customize it
// Get the current theme
UIDefaults defaults = UIManager.getDefaults();
// Change the background color of buttons
defaults.put("Button.background", new Color(70, 130, 180)); // SteelBlue
// Change the foreground (text) color of buttons
defaults.put("Button.foreground", Color.WHITE);
// Change the font of buttons
defaults.put("Button.font", new Font("Segoe UI", Font.BOLD, 14));
} catch (Exception e) {
e.printStackTrace();
}
// Create the GUI with the customized L&F
JFrame frame = new JFrame("Customized Nimbus");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 250);
JButton button1 = new JButton("Custom Button 1");
JButton button2 = new JButton("Custom Button 2");
JLabel label = new JLabel("This label is not affected.");
frame.setLayout(new FlowLayout());
frame.add(button1);
frame.add(button2);
frame.add(label);
frame.setVisible(true);
});
}
}
Using synth.xml (Advanced)
For even more control, you can use the Synth Look and Feel. Synth is a skinning engine that allows you to define the look of every component in an external XML file. You provide the graphics (as images) and define how and where they are painted. This is the most powerful way to customize a Java L&F.
Third-Party Look and Feels
The Java ecosystem has several high-quality, third-party L&Fs that you can add to your project as dependencies.
- FlatLaf: A modern, cross-platform L&F. It's extremely popular, easy to use, and has a clean, flat design that fits well with modern operating systems. It's a great alternative to the default Nimbus.
- Substance: A very mature and highly customizable L&F with many built-in themes and the ability to create your own.
- JGoodies Looks: Another long-standing library with several popular L&Fs.
To use a third-party L&F like FlatLaf, you would:
- Add the dependency to your project (e.g., in Maven or Gradle).
- Set the L&F class name in your code.
Example with FlatLaf (using Maven):
-
Add this to your
pom.xml:<dependency> <groupId>com.formdev</groupId> <artifactId>flatlaf</artifactId> <version>3.2.1</version> <!-- Check for the latest version --> </dependency> -
Use it in your code:
import com.formdev.flatlaf.FlatLightLaf; import javax.swing.*; import java.awt.*; public class FlatLafExample { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { try { // Set the FlatLaf Light theme UIManager.setLookAndFeel(new FlatLightLaf()); } catch (Exception ex) { System.err.println("Failed to initialize FlatLaf"); ex.printStackTrace(); } // Create your GUI... JFrame frame = new JFrame("FlatLaf Example"); // ... rest of your GUI code }); } }
Best Practices Summary
- Set Early: Always set the L&F before creating any
JComponent. - Use
SwingUtilities.invokeLater: All GUI creation and updates must happen on the Event Dispatch Thread (EDT). - Handle Exceptions: Gracefully handle exceptions when setting the L&F.
- Prefer Native: For the best user experience, default to the system's native L&F (
UIManager.getSystemLookAndFeelClassName()). - Customize Wisely: For customization, start with Nimbus and
UIDefaults. For advanced theming, consider Synth or a third-party library like FlatLaf.
