SOLID Principles in Java Explained with Examples | Complete Guide for Beginners & Interviews

๐Ÿ‘๏ธ 2 Views
|
๐Ÿ“… Jul 22, 2026
|
โฑ๏ธ 16 min read
SOLID Principles in Java Explained with Examples | Complete Guide for Beginners & Interviews

SOLID Principles in Java Explained with Real Examples

Writing code that works is only the first step in software development. As applications grow, maintaining, extending, and testing the code becomes more challenging. Poorly designed code often leads to tight coupling, duplicated logic, and bugs that are difficult to fix. This is where the SOLID Principles come into play.

SOLID is a collection of five object-oriented design principles introduced by software engineer Robert C. Martin (Uncle Bob). These principles help developers write clean, maintainable, scalable, and loosely coupled applications. They are widely used in Java development and are commonly discussed during technical interviews.

Whether you are building a small application or a large enterprise system using Spring Boot, following SOLID principles can significantly improve your code quality and make future maintenance easier.


What Does SOLID Stand For?

Letter Principle Purpose
S Single Responsibility Principle A class should have only one reason to change.
O Open/Closed Principle Software should be open for extension but closed for modification.
L Liskov Substitution Principle Subclasses should be replaceable with their parent classes.
I Interface Segregation Principle Clients should not depend on methods they don't use.
D Dependency Inversion Principle Depend on abstractions instead of concrete implementations.

Why Are SOLID Principles Important?

Imagine working on an application that has been under development for several years. Hundreds of classes interact with each other, multiple developers contribute to the project, and new features are added every month. Without proper design principles, even a small change can introduce unexpected bugs.

SOLID principles help organize code into smaller, reusable, and independent components. This makes applications easier to understand, test, extend, and maintain over time.

Benefits of following SOLID principles:

  • Improves code readability.
  • Reduces tight coupling between classes.
  • Encourages code reuse.
  • Makes unit testing easier.
  • Simplifies adding new features.
  • Reduces the chances of introducing bugs.
  • Produces scalable and maintainable applications.

1. Single Responsibility Principle (SRP)

The Single Responsibility Principle states that a class should have only one responsibility or one reason to change.

If a class performs multiple unrelated tasks, modifying one feature may unintentionally affect another. Keeping responsibilities separate makes the code easier to maintain and test.

Real-World Example

Consider a restaurant. The chef prepares food, the cashier handles payments, and the waiter serves customers. If one person tries to perform all these tasks, efficiency decreases and mistakes become more likely.

Similarly, in software development, each class should focus on a single responsibility.


Bad Example

In the following example, the Employee class manages employee information, saves data, and generates reports. This violates the Single Responsibility Principle.

public class Employee {

    public void saveEmployee() {
        System.out.println("Saving employee...");
    }

    public void generateReport() {
        System.out.println("Generating report...");
    }

}

If the database logic changes, this class must be modified. If the report format changes, the same class must be modified again. It now has multiple reasons to change.


Good Example

A better approach is to separate these responsibilities into different classes.

public class Employee {

    private String name;

    public Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}
public class EmployeeRepository {

    public void save(Employee employee) {
        System.out.println("Employee saved.");
    }

}
public class EmployeeReport {

    public void generate(Employee employee) {
        System.out.println("Generating employee report.");
    }

}

Now each class has a single responsibility:

  • Employee stores employee information.
  • EmployeeRepository manages database operations.
  • EmployeeReport generates reports.
Interview Tip: If an interviewer asks, "What is the Single Responsibility Principle?", explain that every class should have only one responsibility and one reason to change. Mention that separating responsibilities improves maintainability, testing, and scalability.

2. Open/Closed Principle (OCP)

The Open/Closed Principle states that software entities such as classes, methods, and modules should be open for extension but closed for modification.

In simple terms, you should be able to add new functionality without changing existing, tested code. Modifying existing code repeatedly increases the risk of introducing bugs, especially in large applications.

Real-World Example

Consider a payment system in an e-commerce application. Initially, the application supports only Credit Card payments. Later, the business wants to add UPI, PayPal, and Net Banking.

If every new payment method requires modifying the existing payment class, the code becomes difficult to maintain. Instead, each payment method should be implemented separately while keeping the existing code unchanged.


Bad Example

public class PaymentService {

            public void pay(String type) {

                if(type.equals("CARD")) {
                    System.out.println("Credit Card Payment");
                }
                else if(type.equals("UPI")) {
                    System.out.println("UPI Payment");
                }
                else if(type.equals("PAYPAL")) {
                    System.out.println("PayPal Payment");
                }

            }

        }
        

Every time a new payment option is introduced, this class must be modified. This violates the Open/Closed Principle.


Good Example

public interface Payment {

    void pay();

}
public class CreditCardPayment implements Payment {

    @Override
    public void pay() {
        System.out.println("Credit Card Payment");
    }

}
public class UpiPayment implements Payment {

    @Override
    public void pay() {
        System.out.println("UPI Payment");
    }

}
public class PaymentProcessor {

    public void process(Payment payment) {
        payment.pay();
    }

}

Usage

PaymentProcessor processor = new PaymentProcessor();

processor.process(new CreditCardPayment());

processor.process(new UpiPayment());

Now, adding a new payment method only requires creating another class that implements the Payment interface. The existing code remains unchanged.

Interview Tip: The Open/Closed Principle is commonly achieved using interfaces, abstract classes, inheritance, and polymorphism.

3. Liskov Substitution Principle (LSP)

The Liskov Substitution Principle states that a subclass should be able to replace its parent class without changing the correctness of the program.

In other words, if class B extends class A, then objects of class B should work correctly wherever objects of class A are expected.

Real-World Example

Imagine a car rental application. If a customer books a vehicle, they shouldn't need to know whether it's a Sedan, SUV, or Hatchback. Every vehicle should support common operations like starting and stopping.

If one vehicle suddenly throws an exception because it cannot perform a basic operation expected from every vehicle, the design violates the Liskov Substitution Principle.


Bad Example

class Bird {

        public void fly() {
            System.out.println("Flying...");
        }

    }

    class Penguin extends Bird {

        @Override
        public void fly() {
            throw new UnsupportedOperationException();
        }

    }
    

A penguin is a bird, but it cannot fly. Replacing a Bird object with a Penguin object causes unexpected behavior. This breaks the Liskov Substitution Principle.


Good Example

Instead of assuming every bird can fly, separate flying behavior from general bird behavior.

class Bird {

        public void eat() {
            System.out.println("Bird is eating.");
        }

    }
    
interface Flyable {

        void fly();

    }
    
class Sparrow extends Bird implements Flyable {

        @Override
        public void fly() {
            System.out.println("Sparrow is flying.");
        }

    }
    
class Penguin extends Bird {

    }
    

Now, only birds that can actually fly implement the Flyable interface. Penguins are no longer forced to provide an invalid implementation.


Why This Design Is Better

  • Every subclass behaves correctly.
  • No unexpected exceptions are thrown.
  • Inheritance represents real-world behavior.
  • The application becomes easier to maintain.
Interview Tip: One of the most common interview examples for LSP is the Bird-Penguin problem. Explain that inheritance should model an "is-a" relationship correctly. If a subclass cannot support the behavior of its parent, inheritance may not be the right design choice.

Key Takeaways

  • SRP focuses on one responsibility per class.
  • OCP allows adding new functionality without modifying existing code.
  • LSP ensures subclasses can safely replace parent classes.

These three principles form the foundation of clean object-oriented design and are frequently discussed during Java and Spring Boot interviews.

4. Interface Segregation Principle (ISP)

The Interface Segregation Principle states that a class should not be forced to implement methods that it does not need. Instead of creating one large interface, create multiple smaller and more specific interfaces.

Large interfaces often become difficult to maintain because implementing classes are forced to provide unnecessary method implementations. Breaking them into focused interfaces keeps the design clean and flexible.

Real-World Example

Imagine an office where different employees have different responsibilities. A printer can print documents, while a scanner can scan documents. Forcing a printer to implement scanning functionality makes no sense.


Bad Example

public interface Machine {

    void print();

    void scan();

}
public class Printer implements Machine {

    @Override
    public void print() {
        System.out.println("Printing...");
    }

    @Override
    public void scan() {
        throw new UnsupportedOperationException();
    }

}

The Printer class is forced to implement the scan() method even though it does not support scanning. This violates the Interface Segregation Principle.


Good Example

public interface Printable {

    void print();

}
public interface Scannable {

    void scan();

}
public class Printer implements Printable {

    @Override
    public void print() {
        System.out.println("Printing...");
    }

}
public class MultiFunctionPrinter
        implements Printable, Scannable {

    @Override
    public void print() {
        System.out.println("Printing...");
    }

    @Override
    public void scan() {
        System.out.println("Scanning...");
    }

}

Now each class implements only the functionality it actually requires, making the code more maintainable and easier to understand.

Interview Tip: Whenever you notice an interface with many unrelated methods, consider splitting it into multiple smaller interfaces.

5. Dependency Inversion Principle (DIP)

The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions such as interfaces or abstract classes.

In simple terms, classes should communicate through interfaces rather than depending directly on concrete implementations. This reduces coupling and makes applications easier to test and extend.

Real-World Example

Think about charging your smartphone. The phone does not depend on a specific charger brand. It depends on a standard charging port such as USB-C. Any compatible charger can be connected without changing the phone.

Similarly, Java classes should depend on interfaces rather than concrete classes.


Bad Example

public class MySQLDatabase {

    public void save() {
        System.out.println("Saving to MySQL");
    }

}

public class UserService {

    private MySQLDatabase database =
            new MySQLDatabase();

    public void saveUser() {
        database.save();
    }

}

If you decide to switch from MySQL to PostgreSQL or MongoDB, you must modify the UserService class.


Good Example

public interface Database {

    void save();

}
public class MySQLDatabase
        implements Database {

    @Override
    public void save() {
        System.out.println("Saving to MySQL");
    }

}
public class UserService {

    private Database database;

    public UserService(Database database) {
        this.database = database;
    }

    public void saveUser() {
        database.save();
    }

}

Usage

Database database = new MySQLDatabase();

UserService service =
        new UserService(database);

service.saveUser();

The UserService class now depends on the Database interface instead of a specific implementation. Replacing the database requires no changes to the service class.

Interview Tip: Dependency Injection in Spring Boot is one of the best real-world examples of the Dependency Inversion Principle.

Benefits of SOLID Principles

  • Improves code readability.
  • Encourages reusable components.
  • Reduces tight coupling.
  • Makes applications easier to test.
  • Simplifies maintenance.
  • Supports scalable software architecture.
  • Makes adding new features easier.
  • Reduces the chances of introducing bugs.

Common Interview Questions

  1. What are the SOLID principles?
  2. Who introduced SOLID principles?
  3. What is the Single Responsibility Principle?
  4. Explain the Open/Closed Principle with an example.
  5. What is the difference between SRP and ISP?
  6. Give a real-world example of LSP.
  7. How does Spring Boot use Dependency Inversion?
  8. Which SOLID principle is achieved through Dependency Injection?

Frequently Asked Questions (FAQ)

Are SOLID principles only used in Java?

No. SOLID principles are language-independent object-oriented design principles and can be applied in languages such as C#, C++, Python, PHP, and Kotlin.

Do I need SOLID principles for small projects?

While small projects may work without them, following SOLID principles from the beginning helps create cleaner and more maintainable code as the project grows.

Are SOLID principles asked in interviews?

Yes. SOLID principles are among the most frequently asked Java and Spring Boot interview topics, especially for mid-level and senior developer roles.


Conclusion

SOLID principles provide a strong foundation for writing clean, scalable, and maintainable Java applications. By following SRP, OCP, LSP, ISP, and DIP, you can reduce code complexity, improve flexibility, and make your applications easier to extend and test.

Whether you are preparing for Java interviews or developing enterprise applications with Spring Boot, mastering SOLID principles will improve your software design skills and help you build high-quality, professional applications.

=

SOLID Principles Cheat Sheet

Principle Short Description Remember As
SRP A class should have only one responsibility. One Class โ†’ One Job
OCP Add new functionality without changing existing code. Extend, Don't Modify
LSP A child class should work wherever its parent is expected. Proper Inheritance
ISP Create small, focused interfaces. Don't Force Unused Methods
DIP Depend on interfaces, not concrete classes. Program to Abstractions

Real-World Examples of SOLID Principles

Principle Real-World Example
SRP A cashier only handles billing, while a chef only prepares food.
OCP Adding a new payment method without changing the existing payment system.
LSP Any compatible USB keyboard should work when connected to a computer.
ISP A printer shouldn't be forced to implement scanning functionality.
DIP A mobile phone works with any compatible USB-C charger instead of one specific brand.

Common Mistakes Beginners Make

  • Creating large classes that handle multiple responsibilities.
  • Using long if-else or switch statements instead of polymorphism.
  • Creating interfaces with too many unrelated methods.
  • Depending directly on concrete classes instead of interfaces.
  • Using inheritance where composition would be a better choice.
  • Ignoring unit testing while designing classes.

Where Are SOLID Principles Used?

SOLID principles are widely used in enterprise Java applications and modern frameworks. You'll frequently see them in:

  • Spring Boot applications
  • REST API development
  • Microservices architecture
  • Android development
  • Banking and financial software
  • E-commerce platforms
  • Hospital management systems
  • Large-scale enterprise applications

SOLID Principles and Design Patterns

SOLID principles often work together with popular design patterns. Many Gang of Four (GoF) design patterns naturally follow one or more SOLID principles.

Design Pattern Related SOLID Principle
Strategy Pattern Open/Closed Principle
Factory Pattern Dependency Inversion Principle
Observer Pattern Dependency Inversion Principle
Decorator Pattern Open/Closed Principle
Adapter Pattern Liskov Substitution Principle

Quick Revision for Interviews

S - Single Responsibility Principle
One class should have one responsibility.

O - Open/Closed Principle
Open for extension, closed for modification.

L - Liskov Substitution Principle
A child class should replace its parent without breaking functionality.

I - Interface Segregation Principle
Use multiple small interfaces instead of one large interface.

D - Dependency Inversion Principle
Depend on abstractions, not concrete implementations.
Pro Tip: In Spring Boot, concepts like Dependency Injection, Service Layer, Repository Layer, and Strategy Pattern are practical examples of SOLID principles. Mentioning these during interviews demonstrates that you understand both the theory and its real-world application.

Final Thoughts

Learning SOLID principles is more than memorizing five definitions. The real value lies in applying these principles while designing classes, writing business logic, and maintaining large codebases. Developers who consistently follow SOLID principles produce cleaner, more reusable, and easier-to-test applications.

As you continue learning Java and Spring Boot, practice identifying SOLID violations in your own projects and refactoring them into cleaner designs. Over time, these principles become a natural part of writing high-quality object-oriented code.