The Chronicle 7 min read
Object-Oriented Design

OOP Concepts Explained for Software Engineering Interviews

Master object-oriented programming fundamentals that every interview tests

L
LLDCanvas Team·Engineering
July 24, 2026
7 min read 68 views 0 likes
Jump to section

Object-oriented programming is not a trivia topic reserved for the first ten minutes of an interview — it is the lens through which every low-level design question gets evaluated. When an interviewer asks you to design a parking lot, a rate limiter, or a splitwise clone, they are really asking: can you organize state and behavior so the system stays correct as it grows? That question is answered entirely by how well you apply encapsulation, abstraction, inheritance, and polymorphism.

This guide walks through each of the four pillars with a concrete before/after Java example, then covers the two decisions that separate a junior design from a senior one: composition vs. inheritance, and interface vs. abstract class. It closes with the questions interviewers actually ask about OOP, so you can check your understanding before walking into the room.


011. Encapsulation

Encapsulation means bundling related data and the behavior that operates on it into a single unit, and restricting direct access to that internal state. It is the difference between a class that merely stores fields and one that protects invariants. If any part of your codebase can reach in and mutate a field without going through validated logic, that class has no real encapsulation, no matter how many methods it has.

java
// Without encapsulation
public class BankAccount {
  public double balance;  // Direct access = no validation
}

BankAccount account = new BankAccount();
account.balance = -1000;  // Allowed! Nothing stops this.

// With encapsulation
public class BankAccount {
  private double balance;

  public void deposit(double amount) {
    if (amount <= 0) {
      throw new IllegalArgumentException("Amount must be positive");
    }
    balance += amount;
  }

  public void withdraw(double amount) {
    if (amount <= 0 || amount > balance) {
      throw new IllegalArgumentException("Invalid withdrawal amount");
    }
    balance -= amount;
  }

  public double getBalance() {
    return balance;
  }
}

The payoff isn't stylistic. Once balance is private and only reachable through deposit/withdraw, the class becomes the single source of truth for what a valid state looks like. You can add logging, transaction limits, or currency conversion inside those methods later without touching a single caller — because callers never depended on the internal representation in the first place.


022. Abstraction

Abstraction exposes only the essential operations a client needs, and hides how those operations are implemented. It is easy to confuse with encapsulation, but the two solve different problems: encapsulation protects state, abstraction simplifies interface. A well-abstracted system lets you swap an entire implementation without any caller noticing.

java
// Without abstraction - client is coupled to a specific provider
public class CheckoutService {
  private final RazorpayClient razorpay = new RazorpayClient();

  public void checkout(Cart cart) {
    razorpay.charge(cart.getTotal(), "INR"); // stuck with Razorpay forever
  }
}

// With abstraction
public interface PaymentGateway {
  boolean processPayment(double amount, String currency);
  PaymentStatus getStatus(String transactionId);
}

public class CheckoutService {
  private final PaymentGateway gateway; // Razorpay, Stripe, or a mock in tests

  public CheckoutService(PaymentGateway gateway) {
    this.gateway = gateway;
  }

  public void checkout(Cart cart) {
    gateway.processPayment(cart.getTotal(), "INR");
  }
}

With the interface in place, CheckoutService never knows or cares which gateway is behind it. That is what makes it trivially testable (inject a fake PaymentGateway) and what lets the business switch payment providers in a region without a rewrite. This is also the mechanism behind the Strategy and Dependency Injection patterns you will use constantly in LLD rounds.

If you want to see abstraction used at scale in a real design, work through the Strategy pattern examples and notice how every one of them hides an interface behind a swappable implementation.


033. Inheritance

Inheritance lets a child class acquire the properties and behavior of a parent class, and is appropriate for genuine is-a relationships — a SavingsAccount is a BankAccount, a Dog is an Animal. Used well, it eliminates duplication. Used to force a relationship that is really "has-a" or "can-do", it creates a rigid hierarchy that breaks the moment requirements shift.

java
// Without inheritance - duplicated logic across account types
public class SavingsAccount {
  private double balance;
  public void deposit(double amount) { /* same validation copy-pasted */ }
}
public class CurrentAccount {
  private double balance;
  public void deposit(double amount) { /* same validation copy-pasted again */ }
}

// With inheritance - shared behavior lives in one place
public abstract class BankAccount {
  protected double balance;

  public void deposit(double amount) {
    if (amount <= 0) throw new IllegalArgumentException("Invalid amount");
    balance += amount;
  }

  public abstract double getInterestRate();
}

public class SavingsAccount extends BankAccount {
  public double getInterestRate() { return 0.04; }
}

public class CurrentAccount extends BankAccount {
  public double getInterestRate() { return 0.0; }
}

The shared deposit validation now lives exactly once, and each subclass only adds what genuinely differs. The danger interviewers are probing for is a hierarchy that goes three or four levels deep to reuse a single method, which tightly couples unrelated classes and makes every change ripple outward. That danger is exactly why the next section exists.


044. Polymorphism

Polymorphism lets objects of different types be treated through a common interface, with the correct behavior selected automatically. It comes in two flavors that interviewers routinely test: compile-time polymorphism (method overloading, resolved by the compiler based on argument types) and runtime polymorphism (method overriding, resolved by the JVM based on the actual object type at execution time).

java
// Runtime polymorphism (overriding)
abstract class Animal {
  abstract void speak();
}
class Dog extends Animal { void speak() { System.out.println("Woof!"); } }
class Cat extends Animal { void speak() { System.out.println("Meow!"); } }
class Bird extends Animal { void speak() { System.out.println("Tweet!"); } }

Animal[] animals = { new Dog(), new Cat(), new Bird() };
for (Animal a : animals) {
  a.speak(); // the correct override runs, decided at runtime
}

// Compile-time polymorphism (overloading)
class Calculator {
  int add(int a, int b) { return a + b; }
  double add(double a, double b) { return a + b; }
  int add(int a, int b, int c) { return a + b + c; }
}

Without polymorphism, the caller in the loop above would need an if (animal instanceof Dog) chain that grows every time a new animal type is added — a classic violation of the Open/Closed Principle. Polymorphism moves that branching decision into the type system itself, so adding a Fish class requires zero changes to existing calling code.


05Composition vs. Inheritance

Interviewers push hard here because Java doesn't allow multiple class inheritance, and real systems constantly need to combine independent behaviors. Consider an amphibious vehicle that needs to both drive and sail: class Amphibious extends LandVehicle, WaterVehicle is a compile error. Composition solves it by having the class hold the behaviors it needs rather than becoming them.

java
public interface Driveable { void drive(); }
public interface Sailable  { void sail();  }

public class AmphibiousVehicle implements Driveable, Sailable {
  private final DriveEngine driveEngine = new DriveEngine();
  private final SailEngine sailEngine = new SailEngine();

  public void drive() { driveEngine.drive(); }
  public void sail()  { sailEngine.sail();  }
}
AspectInheritanceComposition
Relationship modeled"is-a""has-a"
CouplingTight - subclass depends on parent's internalsLoose - depends only on an interface
FlexibilityFixed at compile timeCan swap the contained object at runtime
Multiple behaviorsNot possible for classes in JavaTrivial - implement multiple interfaces
RiskFragile base class problem as hierarchy deepensSlightly more boilerplate (delegation methods)
  • Reach for inheritance only when the relationship is truly "is-a" and the subclass should be usable anywhere the parent is expected (the Liskov Substitution Principle).
  • Reach for composition when you are combining independent capabilities, need to change behavior at runtime, or the hierarchy would otherwise exceed two levels.
  • When in doubt, composition is the safer default - it is easier to compose two small pieces than to untangle a deep inheritance chain later.

This is literally Effective Java, Item 18: "favor composition over inheritance." See it applied across real interview problems in the LLD practice problems.


06Interface vs. Abstract Class

Once you've decided to share behavior through a common type, you still have to pick the mechanism. The two are not interchangeable, and interviewers will ask you to justify the choice.

FeatureInterfaceAbstract Class
Multiple inheritanceYes - a class can implement manyNo - single parent only
ConstructorNoYes
FieldsOnly static final constantsAny field type, including mutable state
Method bodiesDefault/static methods onlyFull implementations allowed
Use whenDefining a pure contract or capabilitySharing partial implementation plus state

07Common OOP Interview Questions

  1. What is the difference between overloading and overriding? Overloading is compile-time polymorphism - same method name, different parameter list, resolved by the compiler within one class. Overriding is runtime polymorphism - a subclass re-implements a parent's method, resolved by the JVM based on the actual object at runtime.
  2. Can a constructor be overridden? No. Constructors are never inherited by subclasses, so "overriding" one is not a valid concept - each class defines its own.
  3. What is the diamond problem, and how does Java avoid it? It's the ambiguity that arises when a class inherits the same method from two parents through multiple inheritance. Java sidesteps it by disallowing multiple class inheritance entirely; when two interfaces provide conflicting default methods, the implementing class is forced to resolve the conflict explicitly.
  4. Why does encapsulation matter if the getters and setters just expose the same field anyway? Encapsulation isn't about hiding the field, it's about controlling the door to it - validation, side effects, and future changes all live behind that one entry point instead of being scattered across every caller.
  5. When would you choose an abstract class over an interface if both support default methods now? Choose the abstract class when subclasses need to share actual state (fields) or a constructor that sets up common initialization; choose the interface when you are only defining a capability a class opts into.

08Conclusion

The four pillars are not independent facts to memorize - they compound. Encapsulation protects the state that abstraction hides behind a clean interface; inheritance and composition are the two competing tools for sharing that abstraction across types; polymorphism is what makes the whole system extensible without rewriting existing callers. An interviewer watching you design a system is really watching whether these four ideas show up naturally in your class diagram, not whether you can recite their definitions.

The fastest way to internalize this is to apply it under time pressure. Take a class you've written recently and ask: does anything reach past its public methods? Does its hierarchy model a real "is-a", or was it shortcut to avoid writing an interface? Answering that honestly, on a handful of real problems, will do more for your interview readiness than reading another list of definitions.

Ready to apply these pillars end-to-end? Work through a full design in the LLD interview roadmap and pressure-test your class design against real interview problems.

Frequently Asked Questions

AEncapsulation, Abstraction, Inheritance, and Polymorphism.

L

LLDCanvas Team

Engineering at LLDCanvas

Ready to practice?

Turn reading into results

110+ LLD interview questions, a live canvas, and timed Interview Mode.

Reader’s Notes (0)

Sign in to join the discussion

Sign in

Loading notes…