Object-oriented programming (OOP) questions appear in almost every developer interview — from junior to staff level. This guide covers the 50 most common OOP interview questions with concise answers, code examples in multiple languages, and comparison tables.
Quick reference
| Topic | Core concepts |
|---|---|
| Four pillars | Encapsulation, Abstraction, Inheritance, Polymorphism |
| Class vs Object | Blueprint vs instance |
| Polymorphism types | Compile-time (overloading) vs runtime (overriding) |
| SOLID principles | SRP, OCP, LSP, ISP, DIP |
| Relationships | Association, Aggregation, Composition, Inheritance |
| Design patterns | Creational, Structural, Behavioural |
Core Concepts
1. What are the four pillars of OOP?
| Pillar | One-line definition |
|---|---|
| Encapsulation | Bundle data + methods; hide internal state behind a public interface |
| Abstraction | Expose only what callers need; hide complexity |
| Inheritance | A class inherits properties and behaviour from a parent class |
| Polymorphism | One interface, many implementations; objects of different types respond to the same message |
2. What is the difference between a class and an object?
A class is a blueprint or template that defines structure and behaviour. An object is a concrete instance of that class created at runtime with its own memory.
class Car: # class (blueprint)
def __init__(self, make):
self.make = make # instance attribute
toyota = Car("Toyota") # object (instance)
honda = Car("Honda") # another object
Every object has its own copy of instance attributes but shares class-level methods.
3. What is encapsulation? How do you achieve it?
Encapsulation bundles data (attributes) and operations (methods) into a single unit and restricts direct access to internal state. You achieve it through access modifiers:
| Modifier | Java/C# | Python convention |
|---|---|---|
| Public | public |
name |
| Protected | protected |
_name |
| Private | private |
__name |
public class BankAccount {
private double balance; // hidden
public double getBalance() { // controlled access
return balance;
}
public void deposit(double amount) {
if (amount > 0) balance += amount;
}
}
4. What is abstraction? How does it differ from encapsulation?
Abstraction is about what an object does — exposing only the relevant interface to callers. Encapsulation is about how it does it — hiding the implementation.
from abc import ABC, abstractmethod
class Shape(ABC): # abstraction: defines what every shape must do
@abstractmethod
def area(self) -> float: ...
class Circle(Shape): # concrete class provides the how
def __init__(self, r):
self.r = r
def area(self):
return 3.14159 * self.r ** 2
Callers only care that shape.area() exists — they don't care about the formula.
5. What is inheritance? What are its types?
Inheritance lets a child class acquire properties and methods from a parent class, enabling code reuse and establishing an "is-a" relationship.
| Type | Description | Support |
|---|---|---|
| Single | One parent | All OOP languages |
| Multilevel | Chain: A → B → C | All OOP languages |
| Hierarchical | Multiple children from one parent | All OOP languages |
| Multiple | One child, multiple parents | Python, C++ (not Java) |
| Hybrid | Combination of the above | Python, C++ |
class Animal:
def speak(self): return "..."
class Dog(Animal): # single inheritance
def speak(self): return "Woof"
class GoldenRetriever(Dog): # multilevel
pass
6. What is polymorphism? What are its types?
Polymorphism allows objects of different types to be treated through a shared interface.
| Type | Also called | When resolved | Mechanism |
|---|---|---|---|
| Compile-time | Static / overloading | Compile time | Method overloading |
| Runtime | Dynamic / overriding | Run time | Method overriding |
// Compile-time polymorphism (overloading)
class Math {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
}
// Runtime polymorphism (overriding)
class Animal { String sound() { return "..."; } }
class Cat extends Animal { String sound() { return "Meow"; } }
Animal a = new Cat();
System.out.println(a.sound()); // "Meow" — decided at runtime
7. What is method overloading vs method overriding?
| Overloading | Overriding | |
|---|---|---|
| Where | Same class | Child class |
| Signature | Different parameters | Same signature |
| Resolution | Compile time | Runtime |
| Inheritance needed? | No | Yes |
| Return type | Can differ (in some languages) | Must be same (or covariant) |
8. What is a constructor? Types of constructors?
A constructor is a special method called when an object is created. It initialises the object's state.
| Type | Description |
|---|---|
| Default | No parameters; provided automatically if none defined |
| Parameterised | Takes arguments to initialise fields |
| Copy | Creates a new object from an existing one (C++) |
| Static / class | Called once per class (Python __init_subclass__, Java static blocks) |
class Point:
def __init__(self, x=0, y=0): # parameterised (default values → also acts as default)
self.x = x
self.y = y
p1 = Point() # default-like
p2 = Point(3, 4) # parameterised
9. What is a destructor?
A destructor is called when an object is garbage collected or goes out of scope. It releases resources held by the object.
class FileHandler:
def __init__(self, path):
self.f = open(path)
def __del__(self): # destructor
self.f.close()
Prefer context managers (with statement / IDisposable in C#) over explicit destructors — they are more predictable.
10. What is the difference between this and super?
| Keyword | Refers to | Common use |
|---|---|---|
this |
Current object | Disambiguate fields, chain constructors |
super |
Parent class | Call parent constructor, access overridden methods |
class Vehicle {
String type;
Vehicle(String type) { this.type = type; }
String info() { return "Vehicle: " + type; }
}
class Car extends Vehicle {
int doors;
Car(String type, int doors) {
super(type); // parent constructor
this.doors = doors; // current object
}
String info() {
return super.info() + " | Doors: " + doors;
}
}
Inheritance & Relationships
11. What is the difference between inheritance and composition?
| Inheritance ("is-a") | Composition ("has-a") | |
|---|---|---|
| Relationship | Dog is an Animal | Car has an Engine |
| Coupling | Tight (child depends on parent) | Loose (depend on interface) |
| Flexibility | Harder to change hierarchy | Swap implementations easily |
| Code reuse | Implicit via hierarchy | Explicit via delegation |
Prefer composition over inheritance (GoF principle) unless a genuine "is-a" relationship exists.
# Composition
class Engine:
def start(self): return "vroom"
class Car:
def __init__(self):
self._engine = Engine() # has-a
def start(self):
return self._engine.start()
12. What is association, aggregation, and composition?
| Relationship | Lifecycle dependency | Example |
|---|---|---|
| Association | Independent | Teacher and Student |
| Aggregation | Child can exist without parent | Department and Employee |
| Composition | Child cannot exist without parent | House and Room |
13. What is multiple inheritance? Why does Java not support it?
Multiple inheritance lets a class inherit from more than one parent. Java excludes it for classes to avoid the Diamond Problem: if classes B and C both inherit from A and define method(), a class D inheriting from both B and C is ambiguous about which method() to use.
Java solves this via interfaces (a class can implement many interfaces).
# Python handles the Diamond Problem via MRO (Method Resolution Order)
class A:
def hello(self): return "A"
class B(A):
def hello(self): return "B"
class C(A):
def hello(self): return "C"
class D(B, C): pass # MRO: D → B → C → A
print(D.__mro__) # shows resolution order
print(D().hello()) # "B"
14. What is an abstract class vs an interface?
| Abstract class | Interface | |
|---|---|---|
| Instantiate? | No | No |
| State (fields) | Yes | No (Java); Yes (Python ABC) |
| Constructor | Yes | No |
| Multiple inheritance | No (Java) | Yes (implement many) |
| Default method | Yes | Yes (Java 8+, Python via ABC) |
| Use when | Share common base code | Define a contract |
abstract class Shape {
String colour; // state allowed
abstract double area(); // subclasses must implement
String describe() { return "I am " + colour; } // concrete method
}
interface Drawable {
void draw(); // contract only (Java < 8)
default void printInfo() { System.out.println("Drawable"); }
}
15. Can you instantiate an abstract class?
No. An abstract class cannot be instantiated directly. You must subclass it and implement all abstract methods.
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self): ...
a = Animal() # TypeError: Can't instantiate abstract class
Access & Modifiers
16. What are access modifiers?
Access modifiers control the visibility of class members.
| Modifier | Java | Accessible from |
|---|---|---|
public |
public |
Everywhere |
protected |
protected |
Same package + subclasses |
| Package-private | (no modifier) | Same package only |
private |
private |
Same class only |
17. What is a static member?
A static member belongs to the class, not to any instance. All instances share the same static field. Static methods can be called without creating an object.
class Counter {
static int count = 0; // shared across all instances
Counter() { count++; }
static int getCount() { return count; }
}
Counter a = new Counter();
Counter b = new Counter();
System.out.println(Counter.getCount()); // 2
18. What is a final keyword (Java)?
| Applied to | Effect |
|---|---|
final variable |
Cannot be reassigned (constant) |
final method |
Cannot be overridden in subclasses |
final class |
Cannot be subclassed (e.g., String) |
19. What is method hiding vs method overriding?
- Method overriding: runtime polymorphism — the child method is invoked based on the actual runtime type.
- Method hiding: when a child class defines a
staticmethod with the same signature as a parent'sstaticmethod — resolved at compile time based on the reference type, not runtime type.
20. What is covariant return type?
An overriding method can return a subtype of the return type declared in the parent. This is covariant return.
class Animal { Animal create() { return new Animal(); } }
class Dog extends Animal {
@Override
Dog create() { return new Dog(); } // Dog is subtype of Animal — OK
}
Polymorphism Deep Dive
21. What is dynamic dispatch?
Dynamic dispatch (also called virtual dispatch) is the mechanism by which the runtime determines which overridden method to call based on the actual object type, not the reference type.
Animal a = new Dog(); // reference type: Animal; runtime type: Dog
a.sound(); // dynamic dispatch → calls Dog.sound()
In C++, you need virtual to enable dynamic dispatch. In Java and Python, all instance methods are virtual by default.
22. What is duck typing?
Duck typing is a polymorphism concept from dynamic languages: "If it walks like a duck and quacks like a duck, it's a duck." The type of an object is determined by its capabilities (methods it has), not its class hierarchy.
class Dog:
def speak(self): return "Woof"
class Robot:
def speak(self): return "Beep"
def make_it_speak(thing):
print(thing.speak()) # no type check needed
make_it_speak(Dog()) # "Woof"
make_it_speak(Robot()) # "Beep"
23. What is operator overloading?
Operator overloading lets you define custom behaviour for operators (+, ==, <, etc.) on your own objects.
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other): # overloads +
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v = Vector(1, 2) + Vector(3, 4) # Vector(4, 6)
Java does not support operator overloading (except + for String).
24. What is upcasting and downcasting?
| Direction | Safety | Example | |
|---|---|---|---|
| Upcasting | Child → Parent | Always safe (implicit) | Animal a = new Dog() |
| Downcasting | Parent → Child | May throw ClassCastException |
Dog d = (Dog) a |
Always check with instanceof before downcasting in Java, or isinstance() in Python.
25. What is an interface default method? Why was it added?
Java 8 added default methods to interfaces to allow adding new methods without breaking existing implementations.
interface Logger {
void log(String msg);
default void logInfo(String msg) { // default — implementations not forced to override
log("[INFO] " + msg);
}
}
Design & Best Practices
26. What are the SOLID principles?
| Letter | Principle | One-line rule |
|---|---|---|
| S | Single Responsibility | A class should have one reason to change |
| O | Open/Closed | Open for extension, closed for modification |
| L | Liskov Substitution | Subtypes must be substitutable for their base type |
| I | Interface Segregation | No client should be forced to depend on methods it doesn't use |
| D | Dependency Inversion | Depend on abstractions, not concretions |
27. What is the Single Responsibility Principle (SRP)?
A class should have one reason to change — it should do one job well.
# Violation: one class handles data + formatting + persistence
class Report:
def calculate(self): ...
def format_html(self): ...
def save_to_db(self): ...
# SRP: split responsibilities
class ReportCalculator: ...
class ReportFormatter: ...
class ReportRepository: ...
28. What is the Open/Closed Principle (OCP)?
Classes should be open for extension but closed for modification. Add behaviour by adding new code (subclass / new implementation), not editing existing, tested code.
# Violation: add a new shape → edit existing method
def area(shape):
if shape.type == "circle": ...
if shape.type == "square": ... # keep modifying
# OCP: use polymorphism
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Circle(Shape): ...
class Triangle(Shape): ... # extend by adding class, no edits to others
29. What is the Liskov Substitution Principle (LSP)?
Objects of a subclass should be replaceable with objects of their superclass without altering the correctness of the program.
// Violation: Square forces both sides equal, breaking Rectangle behaviour
class Rectangle { setWidth(w); setHeight(h); }
class Square extends Rectangle {
setWidth(w) { this.w = this.h = w; } // breaks LSP
}
// Fix: don't extend Rectangle; use a common interface
interface Shape { double area(); }
class Rectangle implements Shape { ... }
class Square implements Shape { ... }
30. What is the Dependency Inversion Principle (DIP)?
High-level modules should not depend on low-level modules. Both should depend on abstractions.
# Violation: OrderService directly depends on MySQLDatabase
class OrderService:
def __init__(self):
self.db = MySQLDatabase() # concrete dependency
# DIP: depend on abstraction
class OrderService:
def __init__(self, db: DatabaseInterface):
self.db = db # inject any implementation
Memory & Object Lifecycle
31. What is the difference between shallow copy and deep copy?
| Shallow copy | Deep copy | |
|---|---|---|
| Copies | Object + references to children | Object + all children recursively |
| Nested objects | Shared | Independent |
| Python | copy.copy() |
copy.deepcopy() |
import copy
original = {"a": [1, 2, 3]}
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original["a"].append(4)
print(shallow["a"]) # [1, 2, 3, 4] — shared reference
print(deep["a"]) # [1, 2, 3] — independent
32. What is garbage collection?
Garbage collection (GC) automatically reclaims memory occupied by objects that are no longer reachable. Common algorithms:
| Algorithm | How it works |
|---|---|
| Reference counting | CPython default; reclaims when count hits 0 |
| Mark-and-sweep | Trace live objects from roots; sweep unmarked (Java GC) |
| Generational GC | Divides objects by age; most objects die young (Java, Python) |
33. What is object pooling?
Object pooling pre-creates a set of objects and reuses them instead of creating/destroying on every request. Reduces GC pressure and allocation cost (e.g., thread pools, database connection pools).
34. What is the difference between stack and heap memory?
| Stack | Heap | |
|---|---|---|
| Stores | Local variables, method frames | Objects, dynamic allocations |
| Managed by | CPU automatically | GC or developer |
| Speed | Very fast | Slower |
| Size | Limited (causes StackOverflow) | Large |
| Thread safety | Per-thread (safe) | Shared (needs synchronisation) |
Design Patterns
35. What are the three categories of design patterns?
| Category | Purpose | Examples |
|---|---|---|
| Creational | Object creation mechanisms | Singleton, Factory, Builder, Prototype |
| Structural | Object composition | Adapter, Decorator, Facade, Proxy |
| Behavioural | Object communication | Observer, Strategy, Command, Iterator |
36. What is the Singleton pattern? What are its drawbacks?
Singleton ensures only one instance of a class exists and provides a global access point.
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
Drawbacks: global state makes testing hard; hides dependencies; not thread-safe without locks; violates SRP.
37. What is the Factory pattern?
Factory pattern provides an interface for creating objects without specifying their exact class.
class AnimalFactory:
@staticmethod
def create(kind: str):
if kind == "dog": return Dog()
if kind == "cat": return Cat()
raise ValueError(f"Unknown animal: {kind}")
a = AnimalFactory.create("dog")
38. What is the Observer pattern?
Observer defines a one-to-many dependency: when one object (subject) changes state, all dependents (observers) are notified automatically.
class EventEmitter:
def __init__(self):
self._listeners = []
def subscribe(self, fn):
self._listeners.append(fn)
def emit(self, data):
for fn in self._listeners:
fn(data)
emitter = EventEmitter()
emitter.subscribe(lambda d: print("Listener 1:", d))
emitter.emit("click")
39. What is the Decorator pattern?
Decorator adds behaviour to objects dynamically without modifying their class.
def bold(fn):
def wrapper(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"
return wrapper
@bold
def greet(name): return f"Hello, {name}"
print(greet("Alice")) # <b>Hello, Alice</b>
40. What is the Strategy pattern?
Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable.
class Sorter:
def __init__(self, strategy):
self._strategy = strategy
def sort(self, data):
return self._strategy(data)
quick_sorter = Sorter(sorted) # built-in
custom_sorter = Sorter(lambda d: d[::-1]) # reverse
Advanced OOP Concepts
41. What is coupling and cohesion?
| Concept | Definition | Goal |
|---|---|---|
| Coupling | Degree of dependency between modules | Low coupling |
| Cohesion | Degree to which elements of a module belong together | High cohesion |
High cohesion + low coupling = maintainable, testable code.
42. What is the Law of Demeter?
Also called the "Principle of Least Knowledge": a method should only call methods on:
- Itself
- Its parameters
- Objects it created
- Its direct component objects
Avoid train wrecks: a.getB().getC().doSomething() — each step adds coupling.
43. What is the difference between early binding and late binding?
| Early binding (static) | Late binding (dynamic) | |
|---|---|---|
| Resolved at | Compile time | Runtime |
| Example | Overloaded methods | Overridden virtual methods |
| Performance | Faster | Slight overhead |
44. What is method chaining / fluent interface?
Method chaining returns this from each method so calls can be chained.
class QueryBuilder:
def __init__(self):
self._parts = []
def select(self, cols):
self._parts.append(f"SELECT {cols}")
return self
def where(self, cond):
self._parts.append(f"WHERE {cond}")
return self
def build(self):
return " ".join(self._parts)
sql = QueryBuilder().select("*").where("id = 1").build()
45. What is the Template Method pattern?
Template Method defines the skeleton of an algorithm in a base class, deferring some steps to subclasses.
class DataProcessor(ABC):
def process(self): # template method
data = self.read()
result = self.transform(data)
self.write(result)
@abstractmethod
def read(self): ...
@abstractmethod
def transform(self, data): ...
@abstractmethod
def write(self, result): ...
46. What is a mixin?
A mixin is a class that provides methods for reuse but is not intended to stand alone or be subclassed. It adds capabilities via multiple inheritance.
class JSONMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class User(JSONMixin):
def __init__(self, name):
self.name = name
u = User("Alice")
print(u.to_json()) # {"name": "Alice"}
47. What is a metaclass?
A metaclass is a class whose instances are classes — it's the class of a class. Used to customise class creation (e.g., enforce interfaces, add methods at class creation time).
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Config(metaclass=SingletonMeta):
pass
48. What is the difference between IS-A and HAS-A relationships?
| Relationship | Implementation | Example |
|---|---|---|
| IS-A | Inheritance | Dog IS-A Animal |
| HAS-A | Composition/Aggregation | Car HAS-A Engine |
Test: can you say "X is a Y" truthfully? If yes, inheritance may be appropriate. If the relationship is "X has a Y" or "X uses a Y", prefer composition.
49. What is the difference between an object and a reference?
In Java and Python, variables hold references (pointers) to objects on the heap — not the objects themselves.
String a = "hello";
String b = a; // b holds the same reference as a
b = "world"; // a still "hello" — reassigned b's reference, not the object
Mutation via a reference does affect the original:
a = [1, 2, 3]
b = a # b refers to same list
b.append(4)
print(a) # [1, 2, 3, 4]
50. What is immutability in OOP?
An immutable object cannot be changed after creation. Immutability makes objects thread-safe, safe to share, and easier to reason about.
| Language | Immutable examples |
|---|---|
| Java | String, Integer, LocalDate |
| Python | str, tuple, int, frozenset |
| JavaScript | const (reference), Object.freeze() |
String s = "hello";
s.toUpperCase(); // returns new String — original unchanged
System.out.println(s); // "hello"
Common OOP mistakes
| Mistake | Problem | Fix |
|---|---|---|
| God class | One class does everything | Split by SRP |
| Anemic domain model | Classes have only getters/setters, no behaviour | Move logic into the class |
| Deep inheritance hierarchies | Fragile, hard to change | Prefer composition |
| Overusing Singleton | Global mutable state, hard to test | Use dependency injection |
| Ignoring encapsulation | Public fields everywhere | Make fields private, expose through methods |
| Primitive obsession | Passing raw strings/ints instead of value objects | Wrap in meaningful types |
| Leaky abstraction | Internal details bleed through the interface | Design interfaces from the caller's perspective |
| Premature inheritance | Inheriting for code reuse, not "is-a" | Use composition or mixins |
OOP vs other paradigms
| OOP | Functional | Procedural | |
|---|---|---|---|
| Core unit | Object | Function | Procedure |
| State | Mutable (encapsulated) | Immutable | Global/local |
| Code reuse | Inheritance, composition | Higher-order functions | Functions |
| Best for | Large systems, domain modelling | Data pipelines, concurrency | Scripts, small programs |
| Examples | Java, C++, Python | Haskell, Erlang, F# | C, Bash |
Many modern languages blend paradigms: Python, Scala, Kotlin, Rust, JavaScript.
Frequently asked questions
Q: Can a class inherit from multiple classes in Java?
A: No — Java supports single inheritance for classes. A class can implement multiple interfaces, which provides similar flexibility without the Diamond Problem.
Q: What is the difference between == and .equals() in Java?
A: == compares references (memory addresses). .equals() compares content. For objects always use .equals() unless you explicitly want reference comparison.
Q: Is Python fully object-oriented?
A: Python is multi-paradigm. Everything is an object (even int and functions), but Python does not force you to use classes — you can write procedural or functional code freely.
Q: What does virtual mean in C++?
A: A virtual function enables runtime polymorphism (dynamic dispatch). Without virtual, calling a method through a base pointer always invokes the base version, even if the object is a derived type.
Q: When should I use inheritance vs composition?
A: Use inheritance only for genuine IS-A relationships where the subclass truly is a specialisation of the parent. Prefer composition for code reuse, flexibility, and easier testing.
Q: What is the difference between an abstract class and an interface in Python?
A: Python does not have a built-in interface keyword. Abstract base classes (ABCs via abc.ABC) serve a similar role. Unlike Java, Python ABCs can have concrete methods and state, so the distinction is less sharp. The convention is to use ABCs when you want to enforce a contract with optional shared implementation.