Top 60 OOPs Interview Questions (2026)

The 60 OOPs questions interviewers actually ask, with direct answers, runnable code, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.

60 questions with answers

What Is OOPs?

Key Takeaways

  • OOPs (Object-Oriented Programming) models software as objects that bundle data (state) with the methods that act on it.
  • Its four pillars are encapsulation, abstraction, inheritance, and polymorphism, and interviews test whether you can explain each with a real example.
  • Interviewers care more about design judgment, when to inherit, when to compose, than about textbook definitions.
  • This page is a question bank: work your tier first, read one tier up, and say answers out loud because delivery is evaluated too.

Object-Oriented Programming (OOPs) is a way of structuring software around objects: self-contained units that hold data and the behavior that operates on that data. Instead of a long list of functions passing values around, you group related state and methods into a class, then create objects (instances) from it. Oracle's official Java documentation frames OOPs through four concepts that show up in nearly every interview: encapsulation (hiding internal state behind a public interface), abstraction (exposing what an object does, not how), inheritance (a class reusing and extending another), and polymorphism (one interface, many behaviors). In interviews, questions probe whether you understand these ideas well enough to apply them, when a subclass is the right tool versus composition, why you'd make a field private, how method overriding differs from overloading. This page collects the 60 questions that come up most, each with a direct answer and runnable code. If a live coding round is next, pair this bank with our AI coding interview prep for the format side.

60Questions with answers on this page
3Difficulty tiers: freshers, intermediate, experienced
4Pillars every OOPs round checks: encapsulation, abstraction, inheritance, polymorphism
10Quiz questions to test yourself

Watch: Python Object Oriented Programming (OOP): For Beginners

Video: Python Object Oriented Programming (OOP): For Beginners (Tech With Tim, YouTube)

Test yourself and earn a certificate

10 quick questions. Score 70%+ to download your OOPs certificate.

Jump to quiz

All Questions on This Page

60 questions
OOPs Interview Questions for Freshers
  1. 1. What is object-oriented programming?
  2. 2. What are the four pillars of OOPs?
  3. 3. What is the difference between a class and an object?
  4. 4. What is encapsulation?
  5. 5. What is abstraction and how does it differ from encapsulation?
  6. 6. What is inheritance?
  7. 7. What is polymorphism?
  8. 8. What is the difference between method overloading and overriding?
  9. 9. What is a constructor?
  10. 10. What are the different types of constructors?
  11. 11. What is a destructor?
  12. 12. What are access modifiers?
  13. 13. What does the this keyword refer to?
  14. 14. What does static mean for a field or method?
  15. 15. What is an abstract class?
  16. 16. What is an interface?
  17. 17. What is the difference between is-a and has-a relationships?
  18. 18. What are getters and setters, and why use them?
  19. 19. What are the types of inheritance?
  20. 20. What does the super keyword do?
OOPs Intermediate Interview Questions
  1. 21. What is the difference between an abstract class and an interface?
  2. 22. Why is composition often preferred over inheritance?
  3. 23. What is the difference between compile-time and runtime polymorphism?
  4. 24. What is the diamond problem in multiple inheritance?
  5. 25. What is a virtual function and how does dynamic dispatch work?
  6. 26. What are the SOLID principles?
  7. 27. Give a concrete example where encapsulation and abstraction differ.
  8. 28. What is the difference between static and dynamic binding?
  9. 29. What are coupling and cohesion?
  10. 30. What is the difference between association, aggregation, and composition?
  11. 31. What is method hiding and how does it differ from overriding?
  12. 32. What does the final (or sealed) keyword do in OOPs?
  13. 33. What is constructor chaining?
  14. 34. What is the difference between a shallow and a deep copy of an object?
  15. 35. What is dependency injection and how does it relate to OOPs?
  16. 36. What is an immutable object and why is it useful?
  17. 37. What practical problem does polymorphism solve?
  18. 38. How does object cloning work and what are its pitfalls?
  19. 39. What are upcasting and downcasting?
  20. 40. What rules must a method follow to correctly override a parent method?
OOPs Interview Questions for Experienced Developers
  1. 41. What are design patterns and can you name a few you've used?
  2. 42. How do you implement a singleton, and what are its downsides?
  3. 43. When would you use a Factory versus a Builder pattern?
  4. 44. Give an example of a Liskov Substitution Principle violation.
  5. 45. How do you apply the Open/Closed Principle in real code?
  6. 46. Why choose the Strategy pattern over subclassing for varying behavior?
  7. 47. How do you handle circular dependencies between classes?
  8. 48. What is a God object and how do you refactor one?
  9. 49. What problems do default methods on interfaces solve, and what do they complicate?
  10. 50. When do SOLID principles hurt more than help?
  11. 51. How do OOPs design choices affect testability?
  12. 52. What is a leaky abstraction and how do you deal with one?
  13. 53. What is the difference between value and reference semantics, and why does it matter for OOPs?
  14. 54. How do you decide the right depth for a class hierarchy?
  15. 55. How can a getter accidentally break encapsulation?
  16. 56. When should you replace conditionals with polymorphism, and when not?
  17. 57. Why do many modern frameworks favor composition and interfaces over deep inheritance?
  18. 58. What are mixins or traits, and what problem do they solve?
  19. 59. How do you design a class to be safely extended by others?
  20. 60. Walk through how you'd model a real domain, say a parking lot, in classes.

OOPs Interview Questions for Freshers

Freshers20 questions

The fundamentals every entry-level round checks. If any answer here surprises you, that's your study list.

Q1. What is object-oriented programming?

OOPs is a way of structuring code around objects that bundle data with the methods that act on it. Instead of scattered functions passing values around, you group related state and behavior into classes and create objects from them.

The goal is code that maps to the real world: a BankAccount object holds a balance and knows how to deposit and withdraw. This keeps related things together, hides internal detail, and makes large systems easier to reason about.

Key point: Open with a one-line definition, then The four pillars. Interviewers use this question to hear whether you can structure an answer.

Watch a deeper explanation

Video: Intro to Object Oriented Programming: Crash Course (freeCodeCamp.org, YouTube)

Q2. What are the four pillars of OOPs?

Encapsulation, abstraction, inheritance, and polymorphism. Encapsulation bundles data with methods and hides internal state. Abstraction exposes what an object does, not how. Inheritance lets a class reuse and extend another. Polymorphism lets one interface drive many behaviors.

Being able to define each and give a real example is the bar. Most OOPs rounds open here and branch out from your answer.

PillarWhat it doesEveryday example
EncapsulationHides state behind an interfaceA car's engine, controlled by a pedal
AbstractionShows what, hides howDriving without knowing combustion detail
InheritanceReuse and extend a classAn ElectricCar is a Car
PolymorphismOne interface, many behaviorsstart() works for any Vehicle

Key point: Pair each pillar with a concrete example. Definitions alone read as memorized; examples read as understood.

Q3. What is the difference between a class and an object?

A class is a blueprint that defines structure (fields) and behavior (methods). An object is a concrete instance created from that class, with its own copy of the state.

One Car class can produce many Car objects, each with a different color and speed. The class is the design; objects are the things you actually work with at runtime.

java
class Car {
    String color;
    Car(String color) { this.color = color; }
}

Car red = new Car("red");   // one object
Car blue = new Car("blue"); // another object, same class

Key point: The follow-up is usually 'can you have a class with no objects?' (yes, abstract or static-only). Have that ready.

Q4. What is encapsulation?

Encapsulation bundles data and the methods that operate on it inside one unit, and hides the internal state behind a public interface. Callers change state through methods, not by touching fields directly.

You do it by making fields private and exposing controlled getters and setters. That lets you validate input, keep invariants, and change the internals later without breaking callers.

java
class Account {
    private double balance;   // hidden state

    public void deposit(double amount) {
        if (amount > 0) balance += amount;  // validated
    }
    public double getBalance() { return balance; }
}

Key point: The point isn't getters and setters, it's control. Say why hiding state matters: validation and freedom to change internals.

Watch a deeper explanation

Video: Object Oriented Programming: The Four Pillars of OOP (Keep On Coding, YouTube)

Q5. What is abstraction and how does it differ from encapsulation?

Abstraction exposes what an object does while hiding how it does it. You drive a car with a pedal and steering wheel without knowing the engine internals. In code, abstract classes and interfaces define a contract and leave the implementation to subclasses.

The distinction from encapsulation: abstraction is about design (what to expose at the interface), encapsulation is about implementation (hiding and protecting state). They work together but answer different questions.

AbstractionEncapsulation
FocusWhat an object doesHow state is protected
LevelDesign and interfaceImplementation detail
Achieved byInterfaces, abstract classesAccess modifiers (private)

Key point: This pair trips up freshers. A crisp 'abstraction is design, encapsulation is implementation' signals real understanding.

Q6. What is inheritance?

Inheritance lets a class (the child or subclass) reuse and extend another class (the parent or superclass). The child gets the parent's fields and methods and can add its own or override existing ones.

It models an is-a relationship: a Dog is an Animal, so Dog extends Animal and inherits eat() while adding bark(). The payoff is code reuse; the risk is tight coupling to the parent, which is why deep hierarchies are discouraged.

java
class Animal {
    void eat() { System.out.println("eating"); }
}

class Dog extends Animal {
    void bark() { System.out.println("woof"); }
}

Dog d = new Dog();
d.eat();   // inherited
d.bark();  // own method

Key point: Say 'is-a' out loud. The natural follow-up is composition (has-a) and when to prefer it.

Q7. What is polymorphism?

Polymorphism means one interface driving many behaviors. The same method call resolves to different implementations depending on the actual object's type. A draw() call on a Shape reference runs Circle.draw() or Square.draw() based on what the object really is.

It comes in two forms: compile-time (method overloading, same name different parameters) and runtime (method overriding, a subclass replacing a parent method). Runtime polymorphism is what makes flexible, extensible designs possible.

java
class Shape { void draw() { System.out.println("shape"); } }
class Circle extends Shape { void draw() { System.out.println("circle"); } }

Shape s = new Circle();
s.draw();   // "circle" - resolved at runtime

Key point: Distinguish overloading from overriding when you answer. Interviewers often ask for both forms in the same breath.

Q8. What is the difference between method overloading and overriding?

Overloading is two or more methods with the same name but different parameter lists in the same class, resolved at compile time. Overriding is a subclass providing its own version of a method it inherited, with the same signature, resolved at runtime.

Overloading is a convenience (one name, several argument shapes). Overriding is the mechanism behind runtime polymorphism.

java
// Overloading: same name, different params
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }

// Overriding: subclass replaces parent method
class Base { String name() { return "base"; } }
class Sub extends Base { String name() { return "sub"; } }
OverloadingOverriding
WhereSame classParent and subclass
SignatureDifferent parametersSame signature
ResolvedCompile timeRuntime
PurposeConvenienceRuntime polymorphism

Key point: A common trap: candidates say return type distinguishes overloads. It doesn't; only the parameter list does.

Q9. What is a constructor?

A constructor is a special method that runs when an object is created, setting up its initial state. It shares the class name and returns no value, not even void.

If you write none, most languages provide a default no-argument constructor. Once you add any constructor, that free default disappears unless you write it yourself.

java
class Point {
    int x, y;
    Point(int x, int y) {   // constructor
        this.x = x;
        this.y = y;
    }
}

Point p = new Point(3, 5);

Key point: Expect 'what is a copy constructor?' and 'what happens if you don't write one?' as follow-ups.

Q10. What are the different types of constructors?

Three common kinds: a default (no-argument) constructor that sets fields to defaults, a parameterized constructor that takes arguments to initialize state, and a copy constructor that builds a new object from an existing one.

Not every language names all three (Java has no built-in copy constructor; you write one), but the concepts carry across.

cpp
class Box {
public:
    int w;
    Box() { w = 0; }              // default
    Box(int width) { w = width; } // parameterized
    Box(const Box& other) { w = other.w; }  // copy
};

Q11. What is a destructor?

A destructor is a method that runs when an object is destroyed, used to release resources it held, such as memory, file handles, or connections. In C++ it's ~ClassName() and runs deterministically when the object goes out of scope.

Languages with garbage collection (Java, Python) don't use classic destructors the same way. Java has no reliable destructor; Python's __del__ exists but timing isn't guaranteed, so both prefer explicit cleanup like try-with-resources or context managers.

cpp
class File {
public:
    File() { /* open */ }
    ~File() { /* close */ }  // runs at scope exit
};

Q12. What are access modifiers?

Access modifiers control who can see a class member. The common set is public (accessible everywhere), private (only inside the class), and protected (the class plus its subclasses). Java adds a package-private default when you write no modifier.

They're the tool that makes encapsulation real: mark state private, expose behavior public, and callers can't reach past your interface.

ModifierSame classSubclassAnywhere
publicYesYesYes
protectedYesYesNo
privateYesNoNo

Q13. What does the this keyword refer to?

this refers to the current object, the instance whose method is running. It disambiguates a field from a parameter of the same name (this.x = x), passes the current object to another method, or chains constructors.

In Python the same role is filled by an explicit self parameter, which is why Python methods list self first.

java
class Timer {
    int seconds;
    Timer(int seconds) {
        this.seconds = seconds;  // field = parameter
    }
}

Q14. What does static mean for a field or method?

A static member belongs to the class itself, not to any single object. One copy is shared across all instances, and you access it through the class name. A static counter shared by every object, or a utility method that needs no instance state, are the usual cases.

A static method can't use this or instance fields, because there's no particular object to refer to. That restriction is the whole point.

java
class Counter {
    static int total = 0;   // shared by all objects
    Counter() { total++; }
}

new Counter();
new Counter();
System.out.println(Counter.total);  // 2

Q15. What is an abstract class?

An abstract class is a class you can't instantiate directly. It can hold both concrete methods (shared implementation) and abstract methods (no body) that subclasses must implement. It exists to be extended.

Use one when several classes share behavior and state but also need their own version of some methods. A Shape base class with a concrete describe() and an abstract area() is the classic example.

java
abstract class Shape {
    abstract double area();          // subclasses fill in
    void describe() {                // shared
        System.out.println("area=" + area());
    }
}
class Circle extends Shape {
    double r;
    Circle(double r) { this.r = r; }
    double area() { return Math.PI * r * r; }
}

Q16. What is an interface?

An interface is a contract: a set of method signatures a class promises to implement, with no state and (traditionally) no implementation. It says what a type can do without saying how.

A class can implement many interfaces, which is how languages like Java get the flexibility of multiple inheritance without its ambiguity. Interfaces are the backbone of loose coupling: code depends on the contract, not the concrete class.

java
interface Payable {
    void pay(double amount);
}

class Invoice implements Payable {
    public void pay(double amount) {
        System.out.println("paid " + amount);
    }
}

Key point: The classic follow-up is 'interface vs abstract class'. It's covered in the intermediate tier; know the difference cold.

Q17. What is the difference between is-a and has-a relationships?

An is-a relationship is inheritance: a Dog is an Animal, so Dog extends Animal. A has-a relationship is composition: a Car has an Engine, so Car holds an Engine object as a field.

The rule of thumb: reach for inheritance only when the subclass truly is a specialized parent; otherwise compose. Misusing inheritance for code reuse is one of the most common design mistakes.

java
// is-a: inheritance
class ElectricCar extends Car { }

// has-a: composition
class Car {
    Engine engine = new Engine();
}

Q18. What are getters and setters, and why use them?

Getters and setters are methods that read and write a private field. They're the controlled doorway to encapsulated state: a setter can validate input, a getter can compute or format on the way out.

The real reason to use them isn't ceremony, it's future-proofing. a plain field, and if validation or logging is needed later, add it inside the accessor without changing a single caller comes first.

java
class User {
    private int age;
    public int getAge() { return age; }
    public void setAge(int age) {
        if (age >= 0) this.age = age;  // validation
    }
}

Q19. What are the types of inheritance?

Single (one child, one parent), multilevel (a chain: C extends B extends A), hierarchical (many children share one parent), and multiple (one child, several parents). Multiple inheritance of classes is where languages diverge: C++ allows it, Java forbids it for classes but allows multiple interfaces.

Knowing which your target language supports matters, because the ambiguity from multiple class inheritance (the diamond problem) is a common follow-up.

  • Single: class B extends A.
  • Multilevel: C extends B extends A.
  • Hierarchical: B and C both extend A.
  • Multiple: D extends B and C (classes: C++ yes, Java no).

Q20. What does the super keyword do?

super refers to the parent class. It calls the parent's constructor (super(args)) or invokes a parent method that the subclass has overridden (super.method()), so you can extend behavior instead of fully replacing it.

Calling super() in a constructor is how a subclass makes sure the parent's initialization runs before its own.

java
class Animal {
    Animal() { System.out.println("animal built"); }
}
class Dog extends Animal {
    Dog() {
        super();   // run parent constructor first
        System.out.println("dog built");
    }
}
Back to question list

OOPs Intermediate Interview Questions

Intermediate20 questions

For candidates with working experience: design judgment, standard trade-offs, and the questions that separate users from designers.

Q21. What is the difference between an abstract class and an interface?

An abstract class can hold state (fields) and concrete methods that subclasses inherit; a class extends only one of them. An interface is a pure contract of method signatures with no state, and a class can implement many.

Choose an abstract class when subclasses share code and state and form an is-a family. Choose an interface when unrelated classes need to promise the same capability. Modern Java blurs this a little with default methods on interfaces, worth mentioning as a nuance.

Abstract classInterface
State (fields)YesConstants only (traditionally)
Method bodiesYesOnly defaults (modern)
How manyExtend oneImplement many
Best forShared code in an is-a familyA capability across unrelated types

Key point: The strongest answer ends with a decision rule, not just a feature table: shared implementation to abstract class, capability contract to interface.

Watch a deeper explanation

Video: Object-Oriented Programming, Simplified (Programming with Mosh, YouTube)

Q22. Why is composition often preferred over inheritance?

Inheritance couples a subclass tightly to its parent's implementation: a change in the base can quietly break every child, and deep hierarchies become rigid. Composition assembles behavior from smaller parts a class holds, so you can swap and combine capabilities without a fragile family tree.

The guideline 'favor composition over inheritance' doesn't ban inheritance; it says reach for it only when there's a true is-a relationship, and prefer has-a assembly the rest of the time.

java
// Inheritance couples Duck to a fixed fly() in the base
// Composition injects the behavior instead:
class Duck {
    private FlyBehavior fly;
    Duck(FlyBehavior fly) { this.fly = fly; }
    void performFly() { fly.execute(); }
}
// Now a rubber duck just gets a NoFly behavior, no override needed.

Key point: The failure mode: a change in the parent breaking subclasses. That concrete pain is what the key point is.

Watch a deeper explanation

Video: The Flaws of Inheritance (CodeAesthetic, YouTube)

Q23. What is the difference between compile-time and runtime polymorphism?

Compile-time (static) polymorphism is method overloading: the compiler picks the method from the argument types before the program runs. Runtime (dynamic) polymorphism is method overriding: the actual object's type decides which method runs, resolved during execution through a mechanism like a virtual method table.

Runtime polymorphism is the one that gives OOPs its flexibility, because you can add new subclasses and existing code calls the right method without changing.

Compile-timeRuntime
MechanismOverloadingOverriding
ResolvedAt compile timeAt execution
Based onArgument typesActual object type
Also calledStatic / early bindingDynamic / late binding

Q24. What is the diamond problem in multiple inheritance?

The diamond problem happens when a class inherits from two classes that share a common ancestor. If both middle classes override a method, the bottom class faces ambiguity about which version it inherits, and the shared ancestor's state can be duplicated.

Languages solve it differently: C++ uses virtual inheritance to share one copy of the ancestor, Java sidesteps it by forbidding multiple class inheritance (interfaces don't carry state, so no ambiguity), and Python resolves method order with a defined MRO.

python
class A:
    def who(self): return 'A'
class B(A):
    def who(self): return 'B'
class C(A):
    def who(self): return 'C'
class D(B, C): pass

print(D().who())      # 'B', decided by the MRO
print([c.__name__ for c in D.__mro__])  # D, B, C, A, object

Q25. What is a virtual function and how does dynamic dispatch work?

A virtual function is one whose call resolves to the object's actual type at runtime, not the reference type. In C++ you mark it virtual; in Java methods are virtual by default. This is what makes overriding work: a Shape reference pointing to a Circle calls Circle.draw().

Under the hood, each class with virtual methods has a table of function pointers (a vtable), and each object carries a pointer to its class's table. The call looks up the right function through that table, which is why it's called dynamic dispatch.

cpp
class Shape {
public:
    virtual void draw() { cout << "shape"; }
};
class Circle : public Shape {
public:
    void draw() override { cout << "circle"; }
};

Shape* s = new Circle();
s->draw();   // "circle" via the vtable

Q26. What are the SOLID principles?

Five design guidelines for maintainable OOPs code. Single Responsibility: a class should have one reason to change. Open/Closed: open for extension, closed for modification. Liskov Substitution: a subclass must be usable wherever its parent is. Interface Segregation: prefer small focused interfaces over fat ones. Dependency Inversion: depend on abstractions, not concrete classes.

Interviewers rarely want all five recited; they want you to spot a violation and fix it. Being able to The principle a piece of bad code breaks is the real test.

  • S: Single Responsibility, one reason to change.
  • O: Open/Closed, extend without modifying.
  • L: Liskov Substitution, subclasses swap in cleanly.
  • I: Interface Segregation, small focused contracts.
  • D: Dependency Inversion, depend on abstractions.

Key point: Have one concrete violation-and-fix ready for at least two letters. Recitation scores low; applied judgment scores high.

Q27. Give a concrete example where encapsulation and abstraction differ.

Take a payment processor. Abstraction is the interface: a pay(amount) method that callers use without knowing whether it's Stripe or PayPal underneath. That's the design decision about what to expose.

Encapsulation is inside the implementation: the Stripe class keeps its API key and retry counter private and validates the amount before charging. That's protecting and hiding the state. Same class, two different ideas working together.

Q28. What is the difference between static and dynamic binding?

Binding is matching a method call to its definition. Static (early) binding happens at compile time, used for private, static, final, and overloaded methods where the target is known without running the program. Dynamic (late) binding happens at runtime for overridden methods, where the object's real type decides.

The distinction explains a common gotcha: a variable's declared type drives overload resolution (static), but its actual object drives override resolution (dynamic).

Q29. What are coupling and cohesion?

Coupling measures how much one class depends on another; cohesion measures how focused a single class is on one job. Good design aims for low coupling (classes change independently) and high cohesion (each class does one thing well).

High coupling means a change ripples across many classes. Low cohesion means a class does too many unrelated things and is hard to name. You reduce coupling with interfaces and dependency injection, and raise cohesion by splitting bloated classes.

CouplingCohesion
MeasuresDependence between classesFocus within a class
You wantLowHigh
Improved byInterfaces, dependency injectionSingle responsibility

Q30. What is the difference between association, aggregation, and composition?

All three describe how objects relate. Association is a general link (a Teacher uses a Classroom). Aggregation is a weak has-a where the part can outlive the whole (a Team has Players, but players exist without the team). Composition is a strong has-a where the part's lifetime is tied to the whole (a House has Rooms; destroy the house and the rooms go with it).

The lifetime question is the deciding line: does the part survive without the whole?

RelationshipStrengthPart outlives whole?
AssociationLoose linkYes
AggregationWeak has-aYes
CompositionStrong has-aNo

Q31. What is method hiding and how does it differ from overriding?

Method hiding happens with static methods: a subclass declares a static method with the same signature as the parent's, and which one runs depends on the reference type, not the object. Overriding is for instance methods and depends on the object's actual type.

The practical warning: hiding looks like overriding but behaves differently, so calling a hidden static method through a parent reference runs the parent's version. It's a frequent trick question.

java
class Base {
    static String label() { return "base"; }
}
class Sub extends Base {
    static String label() { return "sub"; }  // hides, not overrides
}

Base b = new Sub();
System.out.println(b.label());  // "base" - reference type wins

Q32. What does the final (or sealed) keyword do in OOPs?

final locks something down. A final field can't be reassigned after initialization, a final method can't be overridden, and a final class can't be extended. It communicates intent and lets the compiler optimize.

You'd make a class final to prevent subclassing that could break invariants (String is final in Java), and a method final to stop subclasses from changing behavior the base relies on.

Q33. What is constructor chaining?

Constructor chaining is one constructor calling another so initialization logic lives in one place. Within a class you chain with this(args); to a parent you chain with super(args). A parameterized constructor can delegate to a fuller one that does the real setup.

It avoids duplicating field assignments across overloaded constructors. The chain always runs the parent constructor first, then works down.

java
class Box {
    int w, h;
    Box() { this(1, 1); }          // chains to below
    Box(int w, int h) {
        this.w = w;
        this.h = h;
    }
}

Q34. What is the difference between a shallow and a deep copy of an object?

A shallow copy duplicates the object's top-level fields but shares any referenced sub-objects: copying an Order copies the reference to its Customer, so both orders point to the same customer. A deep copy recursively duplicates the referenced objects too, giving a fully independent tree.

The classic bug is mutating a nested object through what you assumed was an independent copy. Deep copy when the object owns its sub-objects; shallow copy when sharing is fine.

python
import copy
class Order:
    def __init__(self, items):
        self.items = items

o1 = Order([1, 2])
shallow = copy.copy(o1)
shallow.items.append(3)
print(o1.items)          # [1, 2, 3] - shared list

deep = copy.deepcopy(o1)
deep.items.append(9)
print(o1.items)          # [1, 2, 3] - independent

Q35. What is dependency injection and how does it relate to OOPs?

Dependency injection means a class receives its collaborators from outside (through the constructor or a setter) instead of creating them itself. A ReportService is handed a Database rather than doing new Database() internally.

It's the practical form of the Dependency Inversion principle: the class depends on an abstraction it's given, which makes it easy to swap a real database for a fake in tests and cuts coupling.

java
class ReportService {
    private final Database db;
    ReportService(Database db) {   // injected, not created
        this.db = db;
    }
}
// tests pass a fake Database; production passes the real one

Q36. What is an immutable object and why is it useful?

An immutable object can't change after construction: all fields are set once and never reassigned. String in Java and tuples in Python are immutable. You build one by making fields final/private, providing no setters, and returning copies of any mutable internals.

The payoff is safety: immutable objects are inherently thread-safe, can be shared freely without defensive copying, and make great map keys because their hash never changes.

java
final class Money {
    private final int cents;
    Money(int cents) { this.cents = cents; }
    int getCents() { return cents; }
    Money add(Money other) {          // returns a new object
        return new Money(this.cents + other.cents);
    }
}

Q37. What practical problem does polymorphism solve?

It lets you write code against a general type and add new specific types without changing that code. A renderer that loops over a list of Shape and calls draw() works for Circle, Square, and any future shape, no if-else on type needed.

That's the open/closed principle in action: the drawing loop is closed for modification but open to new shapes. Without polymorphism you'd have a growing switch statement that every new type forces you to edit.

java
for (Shape s : shapes) {
    s.draw();   // right method per object, no type checks
}

Q38. How does object cloning work and what are its pitfalls?

Cloning creates a copy of an existing object. Java's Cloneable and clone() give a shallow copy by default, which shares mutable sub-objects and surprises people. Many teams avoid clone() entirely in favor of copy constructors or factory methods that make the copy semantics explicit.

The pitfalls: shallow copies sharing nested state, clone() bypassing constructors so invariants can be skipped, and inconsistent behavior across a class hierarchy. Being aware of these is the intermediate-level signal.

Q39. What are upcasting and downcasting?

Upcasting treats a subclass object as its parent type (Animal a = new Dog()); it's always safe and implicit. Downcasting goes the other way (Dog d = (Dog) a), treating a parent reference as a subclass, and it's only safe if the object really is that subclass, otherwise you get a runtime error.

Before downcasting, check with instanceof. Frequent downcasting is often a smell that polymorphism should be doing the work instead.

java
Animal a = new Dog();      // upcast, safe
if (a instanceof Dog) {
    Dog d = (Dog) a;       // downcast, checked
    d.bark();
}

Q40. What rules must a method follow to correctly override a parent method?

The overriding method needs the same name and parameter list, a return type that's the same or a subtype (covariant return), and access that's the same or more permissive, not narrower. It can't throw broader checked exceptions than the parent declares.

Break any of these and it's no longer an override: it becomes an overload, a compile error, or silent method hiding. The @Override annotation exists to catch these mistakes at compile time.

Key point: Mention @Override. Using it shows you've been bitten by a typo silently creating an overload instead of an override.

Back to question list

OOPs Interview Questions for Experienced Developers

Experienced20 questions

advanced rounds probe design judgment, trade-offs, and production scars. Expect every answer here to draw a follow-up.

Q41. What are design patterns and can you name a few you've used?

Design patterns are reusable solutions to common design problems, named so teams can discuss them quickly. The classic groups are creational (Factory, Singleton, Builder), structural (Adapter, Decorator, Facade), and behavioral (Observer, Strategy, Command).

The production signal isn't reciting the catalog; it's naming a pattern you actually used and why. Strategy to swap algorithms at runtime, Observer for event notification, Factory to decouple creation from use. Pair each with a real problem it solved.

Key point: Have two patterns you've genuinely used with the problem they solved. Reciting the GoF list without application indicates textbook, not experience.

Q42. How do you implement a singleton, and what are its downsides?

A singleton guarantees one instance and a global access point: a private constructor, a static holder, and a static accessor. In a threaded context you need lazy initialization done safely, and the initialization-on-demand holder idiom (or an enum in Java) handles that cleanly.

The downsides are real: singletons act as global state, hide dependencies, and make testing hard because you can't easily substitute them. Many teams treat overuse of singletons as a design smell and prefer dependency injection with a single configured instance.

java
class Config {
    private Config() {}
    private static class Holder {
        static final Config INSTANCE = new Config();
    }
    static Config get() { return Holder.INSTANCE; }  // thread-safe, lazy
}

Key point: Volunteering the downsides is the move. Anyone can write a singleton; seniors know why to avoid reaching for it.

Q43. When would you use a Factory versus a Builder pattern?

A Factory decides which class to instantiate and hides that choice from the caller: you ask for a Shape and get back a Circle or Square based on input. A Builder assembles one complex object step by step, useful when a constructor would have many parameters or optional fields.

Rule of thumb: Factory for choosing a type, Builder for constructing a single object with many configuration options. They solve different problems and sometimes appear together.

FactoryBuilder
SolvesWhich type to createHow to assemble one object
Caller seesAn abstract type backA fluent step-by-step API
Good whenType varies by inputMany optional parameters

Q44. Give an example of a Liskov Substitution Principle violation.

The classic one is Square extends Rectangle. A Rectangle lets you set width and height independently; a Square forces them equal. Code that sets a Rectangle's width to 5 and height to 4 expecting area 20 breaks when handed a Square, which silently changes both dimensions.

The fix is to not model it as inheritance: Square and Rectangle share a Shape interface with area(), rather than Square pretending to be a substitutable Rectangle. LSP violations show up as subclasses that surprise code written for the parent.

java
// Violates LSP: a Square breaks Rectangle's contract
Rectangle r = new Square();
r.setWidth(5);
r.setHeight(4);
// caller expects area 20; Square makes it 16

Q45. How do you apply the Open/Closed Principle in real code?

You design so new behavior is added by writing new classes, not editing existing ones. Instead of a discount() method with a growing switch over customer types, define a DiscountStrategy interface and add a new strategy class per rule. The pricing code never changes when a new discount appears.

The trade-off worth naming: over-abstracting for changes that never come adds complexity. Apply Open/Closed where variation is real and expected, not everywhere on principle.

java
interface DiscountStrategy { double apply(double price); }
// add StudentDiscount, SeasonalDiscount as new classes
// the pricing engine loops over strategies and never changes

Q46. Why choose the Strategy pattern over subclassing for varying behavior?

Subclassing to vary one behavior explodes the class count and locks the choice in at compile time: a SortAscending and SortDescending subclass, then variants for every combination. Strategy pulls the varying behavior into its own interface, injected as an object, so you swap it at runtime and combine freely.

It's composition over inheritance applied to algorithms. The object holds a strategy field instead of being a specific subclass, which keeps the hierarchy flat and the behavior interchangeable.

Refactoring a switch to Strategy

1Spot the branching
a switch or if-else selecting behavior by type
2Extract an interface
one method capturing the varying operation
3Make each branch a class
one strategy implementation per case
4Inject and delegate
the context holds a strategy and calls it

The payoff: adding a new behavior means writing a new class, not editing the context. That's Open/Closed made concrete.

Q47. How do you handle circular dependencies between classes?

Circular dependencies (A needs B, B needs A) signal that responsibilities are tangled. The usual fixes: extract the shared logic into a third class both depend on, invert one direction with an interface so A depends on an abstraction B implements, or merge the two if they're really one concept.

In frameworks with dependency injection, circular constructor dependencies fail at startup, which is a useful forcing function. Setter injection or a mediator can break the cycle, but the cleaner design is usually to rethink the boundaries.

Q48. What is a God object and how do you refactor one?

A God object is a class that knows and does too much: hundreds of methods, many unrelated responsibilities, touched by every change. It violates single responsibility and becomes a merge-conflict magnet and a testing nightmare.

You refactor by identifying cohesive clusters of methods and fields, then extracting each into its own focused class, wiring them back through composition. Do it incrementally with tests as a safety net, because a big-bang rewrite of a central class is risky.

Q49. What problems do default methods on interfaces solve, and what do they complicate?

Default methods let an interface ship a method body, so you can add a method to a widely-implemented interface without breaking every implementer. That's how Java evolved the Collection interfaces without a mass migration.

They complicate the abstract-class-versus-interface line and reintroduce a mild multiple-inheritance ambiguity: if two interfaces provide the same default method, the implementing class must resolve it explicitly. Used sparingly for evolution they're fine; used to stuff logic into interfaces they blur design.

Q50. When do SOLID principles hurt more than help?

When applied dogmatically to code that doesn't need it. Splitting every class to satisfy single responsibility can scatter simple logic across ten files. Adding interfaces for dependency inversion where there's only ever one implementation is indirection with no payoff.

The senior view treats SOLID as a response to actual change and pain, not a checklist. You reach for an interface when a second implementation is real or imminent, not preemptively. Naming that trade-off is what separates experience from principle-recitation.

Key point: This question is a trap for dogmatists. Show you apply principles to solve real problems, not to win an argument against a checklist.

Q51. How do OOPs design choices affect testability?

Loose coupling and dependency injection make code testable: if a class receives its collaborators, you pass fakes in tests. Tight coupling, static calls, and singletons make it hard because you can't substitute behavior. Small, single-responsibility classes are easier to test than God objects.

So testability is a design feedback signal. If a class is painful to test, that pain usually points at a coupling or responsibility problem worth fixing, not just a missing mock.

Q52. What is a leaky abstraction and how do you deal with one?

A leaky abstraction is one whose underlying detail forces itself on callers despite the interface trying to hide it. An ORM abstracts SQL until a performance problem makes you reason about the exact queries it generates; the SQL leaks through.

You can't eliminate leaks entirely (all non-trivial abstractions leak somewhat), so you manage them: document the leak, provide an escape hatch (a way to drop to raw queries), and don't pretend the abstraction is total. Pretending it's leak-free is where teams get burned.

Q53. What is the difference between value and reference semantics, and why does it matter for OOPs?

With value semantics, assigning or passing an object copies it, so changes don't propagate. With reference semantics, you share a handle to one object, so a mutation is visible everywhere it's referenced. Most OOPs languages use reference semantics for objects, which is why aliasing bugs happen.

It matters for equality (== on references compares identity, not content, so you override equals/hashCode), for defensive copying at boundaries, and for the case for immutable objects, which sidestep the whole aliasing problem.

java
List<Integer> a = new ArrayList<>(List.of(1, 2));
List<Integer> b = a;   // reference, not a copy
b.add(3);
System.out.println(a); // [1, 2, 3] - same object

Q54. How do you decide the right depth for a class hierarchy?

Keep it shallow. Each level of inheritance couples subclasses to more ancestor behavior, and deep trees become rigid and hard to trace. A good target is one or two levels, with composition doing the rest of the work.

The heuristic: inherit only for true is-a relationships where the subclass is genuinely substitutable, and where the shared behavior is stable. If you're inheriting mainly to reuse code, that's a signal to compose instead. Depth for its own sake is a smell.

Q55. How can a getter accidentally break encapsulation?

By returning a reference to a mutable internal object. A getItems() that hands back the actual internal list lets callers mutate your object's state directly, bypassing every validation and invariant the class tried to protect.

The fix is to return a copy or an unmodifiable view. This is the difference between exposing data and exposing a controlled read, and it's a frequent source of subtle bugs in otherwise well-encapsulated classes.

java
class Cart {
    private final List<Item> items = new ArrayList<>();
    // leaky: caller can mutate internal state
    List<Item> getItemsBad() { return items; }
    // safe: hand back a read-only view
    List<Item> getItems() { return List.copyOf(items); }
}

Q56. When should you replace conditionals with polymorphism, and when not?

Replace a type-based switch or if-else chain with polymorphism when the same branching logic appears in several places and new types are added often; each new type then means one new class instead of edits scattered across the codebase.

Don't do it for a one-off conditional, simple value comparisons, or logic that isn't type-driven. Turning every if into a class hierarchy is over-engineering that hurts readability. The trigger is repeated type-switching, not conditionals in general.

Q57. Why do many modern frameworks favor composition and interfaces over deep inheritance?

Deep inheritance couples applications to framework internals: a base class you must extend dictates your structure and breaks you on framework upgrades. Composition and interfaces let the framework offer capabilities you plug in, so your code depends on small contracts, not a fragile parent.

This is why patterns shifted from extend-our-base-class toward implement-this-interface and annotation-driven wiring. It keeps the framework's evolution and your code loosely joined, which matters a lot across version upgrades.

Q58. What are mixins or traits, and what problem do they solve?

Mixins (Python, Ruby) and traits (Scala, Rust) let you compose reusable behavior into a class without classic single-parent inheritance. A class can pull in several small units of behavior, giving code reuse across unrelated hierarchies.

They solve the reuse problem that single inheritance can't: sharing a capability like serialization or comparison across classes that don't share a parent. The risk is method-resolution complexity when several mixins define the same name, so languages define a clear resolution order.

python
class JsonMixin:
    def to_json(self):
        import json
        return json.dumps(self.__dict__)

class User(JsonMixin):        # pulls in behavior, no is-a
    def __init__(self, name):
        self.name = name

print(User('Asha').to_json())  # {"name": "Asha"}

Q59. How do you design a class to be safely extended by others?

Decide extension points on purpose: mark the methods meant to be overridden, make everything else final or private, and document the contract each overridable method must honor. Never call an overridable method from a constructor, because the subclass override runs before the subclass is fully initialized.

The principle behind it: design and document for inheritance, or prohibit it. A class that's accidentally extensible, with no thought to which methods are safe to override, breaks in surprising ways when someone subclasses it.

Key point: Mentioning the constructor-calling-overridable-method trap is a strong production signal; it bites real codebases and few candidates raise it.

Q60. Walk through how you'd model a real domain, say a parking lot, in classes.

Start from the nouns and their behavior, not a class diagram. A ParkingLot holds Levels; each Level has ParkingSpots of types (compact, large, handicapped); a Vehicle (with subtypes) needs a spot that fits; a Ticket tracks entry time; a pricing strategy computes the fee. Encapsulate spot availability inside Level so nothing else mutates it directly.

Then The design choices out loud: composition for lot-has-levels, an interface for pricing so it's swappable, polymorphism so any Vehicle subtype fits the parking logic. The interviewer is scoring your process, clarify requirements, find entities, assign responsibilities, choose relationships, more than the exact classes.

Key point: Talk through requirements and responsibilities before writing classes; jumping straight to code without clarifying scope loses points.

Back to question list

OOPs vs Procedural Programming

OOPs and procedural programming solve the same problems with different structure. Procedural code organizes logic into functions that pass data around; OOPs binds data and behavior together inside objects. OOPs shines when a system has many entities with shared behavior and state that must stay consistent, because encapsulation and inheritance cut duplication and keep invariants close to the data. Procedural code stays simpler for short scripts, straight-line data pipelines, and cases where the object ceremony adds overhead without payoff. Knowing which fits a given problem, and saying so out loud, is itself an interview signal.

AspectProceduralObject-Oriented (OOPs)
Core unitFunctions and proceduresObjects (data plus methods)
Data and behaviorSeparate; data passed to functionsBundled together in objects
State protectionData often global or shared openlyEncapsulated behind an interface
Code reuseFunction calls, copy-pasteInheritance and composition
Best fitShort scripts, linear data flowLarge systems with many related entities

How to Prepare for a OOPs Interview

Prepare in layers, and practice out loud. Most OOPs rounds move from the four pillars to design questions to a live coding or debugging discussion, so rehearse each stage rather than only reading answers.

  • Nail the four pillars until you can define each and give a real-world example without notes.
  • Practice design trade-offs: inheritance versus composition, when to make a field private, why an interface over a base class.
  • Type and run the code snippets; modifying a working class hierarchy cements it far faster than reading.
  • Take the quiz on this page to find weak spots, then revisit those questions before the real round.

The typical OOPs interview flow

1The four pillars
define encapsulation, abstraction, inheritance, polymorphism with examples
2Design questions
inheritance vs composition, SOLID, when to use an interface
3Live coding
model a small domain in classes and explain your choices
4Debugging or review
spot a broken hierarchy, fix tight coupling, reason about a design

Earlier rounds increasingly run as AI coding interviews. The pillars and design judgment on this page are what gets probed at every stage.

Test Yourself: OOPs Quiz

Ready to test your OOPs knowledge?

10 questions, about 6 minutes. Score 70% or higher to earn a shareable certificate.

10 questions Instant feedback Free certificate on 70%+

Frequently  Asked  Questions

Which OOPs topics should I prioritize before a technical round?

Prioritize the areas that change real decisions: fundamentals, practical tasks, debugging, trade-offs, and evidence. The highest-value questions are tied to a project example, a validation check, or a failure mode.

Which language do these answers use?

Examples use whichever language shows the concept most clearly, mostly Java and Python, with a little C++ and JavaScript where it fits. The concepts are language-independent, so read the idea first and the syntax second. In your interview, answer in the language on the job description.

What are the four pillars of OOPs I must know?

Encapsulation (bundling data with methods and hiding internal state), abstraction (exposing what an object does, not how), inheritance (a class extending another), and polymorphism (one interface, many behaviors). Nearly every OOPs round opens here, so be able to define each and pair it with a real example.

Are these questions enough to pass a OOPs interview?

They cover the concept and design portion well, but many rounds also include live coding: modeling a small domain in classes while explaining your choices. designing a system out loud with a timer is the practice path. Our AI coding interview prep guide covers that format, including how automated evaluation reads your process.

How long does it take to prepare for a OOPs interview?

If you write object-oriented code at work or in study, one to two weeks of an hour a day covers this bank with practice runs. Starting colder, plan three to four weeks and design small systems daily; reading definitions without coding them is how preparation quietly fails.

Is there a way to test my OOPs knowledge?

Yes. The quiz on this page scores you as you go and explains every answer, so you find your weak spots fast. Pass the threshold and you can download a shareable certificate with your name and score, free and with no sign-up. It's the quickest way to check yourself before the real round.

From the team that builds coding interviews

Hyring builds the AI Coding Interviewer that runs live technical rounds in 20+ languages for 5,000+ hiring teams. These OOPs questions and scoring notes reflect what actually gets asked and evaluated inside the interviews we host.

See how the AI Coding Interviewer works

Sources

Adithyan RKWritten by Adithyan RK
Surya N
Fact-checked by Surya N
Published on: 5 May 2026Last updated: 14 Jul 2026
Share: