- 50 OOP questions grouped by topic, each with an answer short enough to say in an interview and code where code proves the point.
- Most marks are lost on four distinctions: overloading vs overriding, abstract class vs interface, abstraction vs encapsulation, and inheritance vs composition.
- Experienced candidates get probed on dynamic dispatch internals (vtables), Liskov violations, immutability and the
equals/hashCodecontract. - The seven tricky output questions at the end (static hiding, field hiding, constructor calling overridden methods, overload resolution with
null) are the most common traps.
OOP questions appear in nearly every software interview in India: as a full technical round at TCS, Infosys, Wipro or Cognizant, as warm-up questions at product companies, and as the foundation under every low-level design round. The questions are predictable. What separates candidates is whether the answer sounds like a definition recited from notes or like someone who has actually written classes and hit the edge cases.
Answers below are written in the way you would say them, followed by code where a snippet settles the question. Examples are in Java (the most common interview language for OOP), with C++ and Python notes where the languages differ in an interview-relevant way.
How OOP Is Tested in Interviews
Freshers get definition questions plus "give a real example": what is polymorphism, show me overloading, why can't we instantiate an abstract class. Experienced engineers (2+ years) get "why" and "what breaks" questions: why composition over inheritance, how dynamic dispatch works, what happens if you override equals but not hashCode, design an immutable class. Both groups get output-prediction snippets.
Four Pillars of OOPs: Questions 1 to 10
Q1. What is object-oriented programming?
A paradigm that models software as objects, each combining state (fields) and behaviour (methods), interacting through well-defined interfaces. The goal is to manage complexity: changes stay local to the object that owns the data, and new behaviour can be added by adding types rather than editing existing code.
Q2. What are the four pillars of OOP?
Encapsulation (bundle data with methods and restrict direct access), abstraction (expose what, hide how), inheritance (derive a type from another to reuse and specialise) and polymorphism (one interface, many implementations).
Q3. What is encapsulation? Give a real example.
Encapsulation hides internal state and forces all changes through methods that enforce invariants. A bank account should never have a negative balance, so balance is private and only deposit/withdraw can change it.
public class BankAccount {
private long balancePaise; // hidden state
public void deposit(long paise) {
if (paise <= 0) throw new IllegalArgumentException("amount must be positive");
balancePaise += paise;
}
public void withdraw(long paise) {
if (paise > balancePaise) throw new IllegalStateException("insufficient funds");
balancePaise -= paise;
}
public long getBalancePaise() { return balancePaise; } // read-only view, no setter
}
Note the deliberate absence of setBalance. Private fields plus a public getter and setter for each one is not real encapsulation; it just adds ceremony around public fields.
Q4. What is abstraction?
Showing only essential behaviour and hiding implementation detail. A PaymentProcessor interface exposes pay(order); the caller does not know whether it talks to UPI, a card network or a wallet. In Java, abstraction is achieved with interfaces and abstract classes.
Q5. Abstraction vs encapsulation: what is the difference?
Abstraction is a design-level decision about the outside view (what operations exist). Encapsulation is an implementation-level mechanism to protect the inside (access modifiers, invariants). A TV remote is abstraction (buttons, not circuits); the sealed plastic case is encapsulation.
Q6. What is inheritance and what types exist?
A class acquires fields and methods of a parent and can override or extend them. Types: single, multilevel (A to B to C), hierarchical (many children of one parent), multiple (one child, many parents) and hybrid. Java supports multiple inheritance only through interfaces; C++ supports it for classes; Python supports it with a defined method resolution order (MRO).
Q7. What is polymorphism?
The ability of one interface to take many forms. Shape s = new Circle(); s.area(); calls Circle.area(), and the same line with a Square calls Square.area(). Types are covered in Q20.
Q8. What is the diamond problem and how do languages handle it?
If B and C both inherit from A and override hello(), and D inherits from both, which hello() does D get? Java forbids multiple class inheritance; with interfaces, if two default methods clash the class must override and may delegate explicitly. C++ uses virtual inheritance to share one A subobject and requires disambiguation. Python uses the C3 linearised MRO (D.__mro__).
interface Walker { default String move() { return "walk"; } }
interface Swimmer { default String move() { return "swim"; } }
class Duck implements Walker, Swimmer {
@Override public String move() { // compile error without this override
return Walker.super.move() + " and " + Swimmer.super.move();
}
}
Q9. What are access modifiers in Java?
| Modifier | Same class | Same package | Subclass (other package) | Everywhere |
|---|---|---|---|---|
private | Yes | No | No | No |
| default (none) | Yes | Yes | No | No |
protected | Yes | Yes | Yes (via inheritance) | No |
public | Yes | Yes | Yes | Yes |
C++ has private, protected, public (and friend). Python has no enforced privacy: _x is a convention and __x triggers name mangling to _ClassName__x.
Q10. What are coupling and cohesion?
Cohesion is how focused a class is on one responsibility; aim high. Coupling is how much a class depends on others' internals; aim low. A UserService that also formats invoices and sends SMS has low cohesion; a class calling order.getCustomer().getAddress().getCity() is tightly coupled to three structures. These two ideas underlie the SOLID principles.
Classes, Objects and Constructors: Questions 11 to 19
Q11. Class vs object?
A class is a blueprint (type definition); an object is an instance with its own state in memory. One Car class, many car objects. In Java the class's metadata lives in the method area (Metaspace), objects live on the heap.
Q12. What is a constructor and what types exist?
A special method that initialises a new object; it has the class name and no return type. Types: default (compiler-provided only if you write none), no-arg, parameterised, and in C++ a copy constructor (Java has no built-in copy constructor, though you can write one). Constructors can be overloaded but not overridden or inherited.
Q13. Can a constructor be private? Why would you do that?
Yes. Private constructors block outside instantiation, used in Singletons, static factory methods (List.of()), builders, and utility classes like java.lang.Math. See the design patterns guide for the thread-safe Singleton.
Q14. What are this and super?
this refers to the current object: used to disambiguate fields from parameters and to chain constructors (this(...)). super refers to the parent part: to call an overridden parent method (super.save()) or a parent constructor (super(...)). Traditionally this(...) or super(...) must be the first statement of a constructor; recent Java versions relax this slightly for statements that do not touch the object, but in interviews the first-statement rule is the expected answer.
Q15. What does the static keyword mean?
A static member belongs to the class, not to any instance. Static fields are shared by all objects (e.g., an instance counter); static methods cannot access instance members directly because there is no this. Static blocks run once when the class is initialised.
Q16. What is the difference between final, finally and finalize?
final: a variable cannot be reassigned, a method cannot be overridden, a class cannot be extended. finally: a block after try that runs whether or not an exception occurs. finalize(): an old GC hook, deprecated since Java 9 and marked for removal; use try-with-resources or Cleaner instead.
Q17. Shallow copy vs deep copy?
A shallow copy duplicates the outer object but shares referenced objects; a deep copy duplicates the whole graph. If Order holds a List<Item>, a shallow copy means modifying items in the copy changes the original. Object.clone() is shallow by default, which is one reason many style guides prefer copy constructors.
Q18. How do you design an immutable class?
- Declare the class
final(or make constructors private with factories) so subclasses cannot add mutability. - Make all fields
private final. - No setters.
- Defensively copy mutable inputs in the constructor and mutable outputs in getters.
import java.util.List;
public final class Invoice {
private final String id;
private final List<String> lineItems;
public Invoice(String id, List<String> lineItems) {
this.id = id;
this.lineItems = List.copyOf(lineItems); // defensive, unmodifiable copy
}
public String getId() { return id; }
public List<String> getLineItems() { return lineItems; } // already unmodifiable
}
Benefits: inherently thread-safe, safe as HashMap keys, easy to reason about. String, Integer and Java record types are immutable (records are shallowly immutable: a record holding a mutable list still needs a defensive copy).
Q19. What is the equals/hashCode contract?
If a.equals(b) then a.hashCode() == b.hashCode(). The reverse need not hold. Override equals without hashCode and two "equal" objects land in different HashMap buckets, so map.get(equalKey) returns null and a HashSet stores duplicates. This is a favourite experienced-level question; the HashMap internals guide explains the bucket mechanics.
Polymorphism Interview Questions: 20 to 28
Q20. What are the types of polymorphism?
| Type | Resolved | Mechanism | Example |
|---|---|---|---|
| Compile-time (static, ad-hoc) | Compile time | Method overloading, operator overloading (C++) | add(int,int), add(double,double) |
| Runtime (dynamic, subtype) | Runtime | Method overriding + dynamic dispatch | Shape s = new Circle(); s.area() |
| Parametric | Compile time | Generics (Java), templates (C++) | List<T>, std::vector<T> |
Most interviewers only expect the first two; mentioning parametric polymorphism is a small bonus.
Q21. Overloading vs overriding?
| Overloading | Overriding | |
|---|---|---|
| Where | Same class (or inherited) | Subclass redefines parent method |
| Signature | Same name, different parameter list | Same name and parameters |
| Return type | Can differ (but alone cannot distinguish) | Same or covariant (subtype) |
| Binding | Compile time, on static types | Runtime, on actual object type |
| Access | Any | Cannot be more restrictive |
| Checked exceptions | Any | Cannot throw new or broader checked exceptions |
Q22. Can we overload by return type alone?
No. int get() and String get() in one class is a compile error because a call like get(); gives the compiler no way to choose.
Q23. Can static, private or final methods be overridden?
None of them. Static methods are hidden, not overridden (resolved by reference type). Private methods are invisible to the subclass, so a same-named method in the child is a new, unrelated method. Final methods explicitly forbid overriding.
Q24. How does dynamic dispatch work internally?
Conceptually, each class has a virtual method table (vtable): an array of pointers to the method implementations for that class. Each object carries a pointer to its class's metadata. A virtual call looks up the slot for that method in the object's table and jumps there. C++ compilers implement exactly this, with a hidden vptr per object of a polymorphic class. The JVM uses similar tables, and the JIT often removes the indirection entirely through inlining when only one implementation has been loaded (monomorphic call sites).
Q25. Why must a base class destructor be virtual in C++?
If you delete a derived object through a base pointer and the base destructor is non-virtual, the behaviour is undefined; in practice only the base destructor runs and derived resources leak.
#include <iostream>
#include <memory>
struct Base {
virtual ~Base() { std::cout << "~Base\n"; } // remove 'virtual' and ~Derived is skipped
};
struct Derived : Base {
int* buf = new int[1024];
~Derived() override { delete[] buf; std::cout << "~Derived\n"; }
};
int main() {
std::unique_ptr<Base> p = std::make_unique<Derived>();
} // prints ~Derived then ~Base
Q26. What is object slicing?
In C++, assigning or passing a derived object by value as a base type copies only the base part; the derived part is "sliced off" and virtual calls on the copy hit the base implementation. Pass polymorphic objects by reference or pointer. Java has no slicing because objects are always handled through references.
Q27. What are upcasting and downcasting?
Upcasting (child to parent reference) is implicit and always safe. Downcasting (parent reference to child type) needs an explicit cast and throws ClassCastException if the object is not actually that type. Guard it with instanceof; Java 16+ pattern matching makes this concise: if (shape instanceof Circle c) { c.radius(); }. Frequent downcasting is a design smell: the behaviour probably belongs in an overridden method.
Q28. What is a covariant return type?
An overriding method may return a subtype of the parent's return type. If Animal.reproduce() returns Animal, Dog.reproduce() may return Dog. This avoids casts at the call site.
Abstract Class vs Interface: Questions 29 to 36
Q29. What is the difference between an abstract class and an interface?
| Aspect | Abstract class | Interface (Java 8+) |
|---|---|---|
| State | Any fields, including mutable instance fields | Only public static final constants |
| Constructors | Yes (called via super) | No |
| Methods | Abstract and concrete, any access level | Abstract, default, static, and private helpers (Java 9+) |
| Inheritance | A class extends only one | A class implements many |
| Models | "Is-a" with shared code and state | "Can-do" capability or contract |
| Example | AbstractList, HttpServlet | Comparable, Runnable, List |
Q30. When would you choose an abstract class over an interface?
When subclasses share state and a fixed algorithm skeleton. Example: every report generator fetches data, formats it, and writes a header and footer. An abstract ReportGenerator holds the template (generate() calling abstract fetch() and format()), which is the Template Method pattern. Choose an interface when unrelated classes need a common capability (Comparable, AutoCloseable) or when you want multiple inheritance of type. A common modern combination is an interface for the contract plus an abstract base class for convenience, as the JDK does with List and AbstractList.
Q31. Can an abstract class have a constructor? Can it be instantiated?
It can have constructors, which initialise its fields when a subclass is created. It cannot be instantiated directly with new, although new Shape() { ... } creates an anonymous subclass, which is legal.
Q32. Can an abstract class have zero abstract methods?
Yes. Marking it abstract simply prevents instantiation. HttpServlet is abstract yet has no abstract methods; you are expected to override the ones you need.
Q33. Why were default methods added to interfaces?
For interface evolution. Java 8 needed to add stream() and forEach() to Collection without breaking every existing implementation in the world. Default methods provide a body that implementing classes inherit unless they override.
Q34. What is a marker interface?
An interface with no methods that tags a class for special treatment, such as Serializable and Cloneable. Annotations largely replaced the idea, but marker interfaces have one advantage: they define a type the compiler can check.
Q35. What is a functional interface?
An interface with exactly one abstract method, which lets you implement it with a lambda or method reference: Runnable, Comparator, Function<T,R>. @FunctionalInterface asks the compiler to enforce the single-method rule.
Q36. How is an interface done in C++ and Python?
C++: a class with only pure virtual functions (virtual void draw() = 0;) and a virtual destructor. Python: an abstract base class using abc.ABC and @abstractmethod, or structurally with typing.Protocol. Python also relies heavily on duck typing: if it has a read() method, it can be used as a file.
Inheritance vs Composition: Questions 37 to 43
Q37. What is composition over inheritance?
Build behaviour by holding references to other objects (has-a) instead of extending classes (is-a). Inheritance is the tightest coupling in OOP: the child depends on the parent's implementation details, and the relationship is fixed at compile time.
The classic JDK cautionary tale: java.util.Stack extends Vector. Because of that inheritance, a Stack exposes add(int index, E e) and get(i), letting callers insert into the middle of a stack, which breaks the abstraction. A composed stack would hold a list privately and expose only push, pop and peek. That is why Java recommends ArrayDeque for stacks.
// Inheritance: a Logger hierarchy explodes as features combine
// FileLogger, TimestampedFileLogger, EncryptedTimestampedFileLogger...
// Composition: combine independent pieces at runtime
interface LogSink { void write(String line); }
interface LogFormatter { String format(String msg); }
class Logger {
private final LogSink sink;
private final LogFormatter formatter;
Logger(LogSink sink, LogFormatter formatter) { this.sink = sink; this.formatter = formatter; }
void log(String msg) { sink.write(formatter.format(msg)); }
}
// new Logger(line -> System.out.println(line), m -> java.time.Instant.now() + " " + m)
Q38. Association vs aggregation vs composition?
All are "has-a". Association: a general relationship (Teacher teaches Student). Aggregation: whole-part where the part can exist independently (a Team has Players; a disbanded team leaves players). Composition: whole-part with owned lifecycle (a House has Rooms; destroy the house, the rooms go). In UML, aggregation is a hollow diamond and composition a filled one.
Q39. What is the Liskov Substitution Principle? Give a violation.
Objects of a subclass must be usable wherever the parent is expected without breaking correctness. Classic violation: Square extends Rectangle. Code that calls setWidth(5); setHeight(4); and expects area 20 gets 16 from a Square, because Square must keep sides equal. Mathematically a square is a rectangle; behaviourally, a mutable Square is not a substitutable Rectangle. Fix: separate types implementing a common Shape, or make them immutable.
Q40. What is the fragile base class problem?
Changes to a parent class silently break subclasses. The textbook example (from Effective Java): a subclass of HashSet counts additions by overriding both add and addAll. Because HashSet.addAll internally calls add, each element is counted twice. The subclass depended on an implementation detail it could not see.
Q41. Is-a vs has-a: how do you decide in an LLD round?
Ask: "Is every X always a Y, everywhere a Y is used, for its whole life?" A Car is a Vehicle: inherit. A Car has an Engine that might be swapped for an ElectricMotor: compose. A Manager "is an" Employee today but could become an individual contributor tomorrow: model the role as a field, not a subclass.
Q42. What is method hiding vs overriding?
Static methods with the same signature in parent and child hide each other; which one runs depends on the reference type at compile time. Instance methods override; which one runs depends on the object at runtime. See tricky question Q44.
Q43. What is dependency injection and how does it relate to OOP?
Instead of a class constructing its collaborators (new MySqlRepo()), they are passed in, usually through the constructor, typed as interfaces. This is composition plus the Dependency Inversion Principle, and it makes classes testable with fakes. Spring and Guice automate the wiring, but plain constructor injection needs no framework.
Tricky OOPs Output Questions: 44 to 50
These snippets appear in written tests and live rounds. Predict the output before reading the answer.
Q44. Static method hiding vs instance overriding
class A {
void show() { System.out.println("A.show"); }
static void hello() { System.out.println("A.hello"); }
}
class B extends A {
@Override void show() { System.out.println("B.show"); }
static void hello() { System.out.println("B.hello"); }
}
public class Main {
public static void main(String[] args) {
A a = new B();
a.show();
a.hello();
}
}
Output: B.show then A.hello. Instance method: dynamic dispatch on the object (B). Static method: resolved by reference type (A). Compilers warn about calling static methods via instances for exactly this reason.
Q45. Fields are not polymorphic
class Parent { String name = "parent"; String getName() { return name; } }
class Child extends Parent { String name = "child"; @Override String getName() { return name; } }
Parent p = new Child();
System.out.println(p.name + " " + p.getName());
Output: parent child. Field access uses the reference type; the method is dispatched to Child. The Child object actually contains two name fields. Field hiding is legal but confusing; avoid it.
Q46. Calling an overridden method from a constructor
class Base {
Base() { describe(); }
void describe() { System.out.println("Base"); }
}
class Derived extends Base {
private int x = 42;
@Override void describe() { System.out.println("Derived x=" + x); }
}
new Derived();
Output: Derived x=0. The Base constructor runs before Derived's field initialisers, but dispatch already targets Derived, so it sees the default value 0. If x were an object it would be null, a classic source of NullPointerExceptions. Rule: never call overridable methods from constructors. (In C++ the same code prints Base, because during base construction the object's dynamic type is still Base.)
Q47. Overload resolution with null
static void f(Object o) { System.out.println("Object"); }
static void f(String s) { System.out.println("String"); }
f(null); // ?
static void g(String s) { System.out.println("String"); }
static void g(Integer i) { System.out.println("Integer"); }
// g(null); // ?
Answer: f(null) prints String because the compiler picks the most specific applicable overload, and String is a subtype of Object. g(null) does not compile: "reference to g is ambiguous", since neither String nor Integer is more specific.
Q48. Widening beats boxing
static void h(long x) { System.out.println("long"); }
static void h(Integer x) { System.out.println("Integer"); }
static void h(int... x) { System.out.println("varargs"); }
h(5);
Output: long. Java resolves overloads in phases: first without boxing or varargs (widening int to long is allowed here), then with boxing, then with varargs. The first phase that finds a match wins.
Q49. Initialisation order with static and instance blocks
class Parent {
static { System.out.println("1 Parent static"); }
{ System.out.println("3 Parent instance block"); }
Parent() { System.out.println("4 Parent constructor"); }
}
class Child extends Parent {
static { System.out.println("2 Child static"); }
{ System.out.println("5 Child instance block"); }
Child() { System.out.println("6 Child constructor"); }
}
new Child();
new Child();
Output: lines 1 to 6 in order for the first object, then only 3, 4, 5, 6 for the second. Static blocks run once at class initialisation (parent first). For each object, the parent's instance blocks and constructor complete before the child's.
Q50. C++ object slicing in action
#include <iostream>
#include <string>
struct Animal {
virtual std::string speak() const { return "..."; }
virtual ~Animal() = default;
};
struct Dog : Animal {
std::string speak() const override { return "Woof"; }
};
void byValue(Animal a) { std::cout << a.speak() << "\n"; }
void byRef(const Animal& a) { std::cout << a.speak() << "\n"; }
int main() {
Dog d;
byValue(d); // ?
byRef(d); // ?
}
Output: ... then Woof. Passing by value copy-constructs an Animal, slicing away the Dog part, so the virtual call resolves to Animal::speak. Passing by reference preserves the dynamic type.
self.name always finds the instance attribute, which the most recent assignment set. Know which language your interviewer is assuming.How to Prepare OOPs for Placements and Product Company Interviews
Reading 50 answers is not the same as being able to defend them under follow-ups. A focused plan:
- Day 1: say each pillar with an example from a real app. Practise out loud: encapsulation with a wallet balance, abstraction with a payment interface, inheritance with vehicle types, polymorphism with notification channels.
- Day 2: run every snippet in this post. Change one thing in each (remove
virtual, make the method static, pass by value) and predict the new output first. This is how the tricky-output questions stop being tricky. - Day 3: build one small system with interfaces. A parking lot or library management system in about 300 lines forces you to choose abstract class vs interface and inheritance vs composition for real. Use the library management LLD as a reference after attempting it.
- Day 4: connect to SOLID and patterns. Most experienced-level OOP questions are SOLID in disguise, and patterns are OOP applied to recurring problems. Skim the top 10 design patterns.
- Day 5: language-specific depth. For Java, the Java interview questions guide; for C++, the C++ interview questions guide, especially virtual functions, the rule of three/five and smart pointers.
Red flags interviewers notice
- Defining polymorphism as "many forms" and stopping there, with no example.
- Saying "interfaces cannot have method bodies" in a Java 8+ world.
- Claiming encapsulation means "getters and setters for every field".
- Using inheritance purely to reuse code (a
Customer extends DatabaseConnectiontype of design). - Not knowing what breaks when
hashCodeis not overridden alongsideequals.
Get these right and OOP becomes a round where you bank points rather than lose them, and it gives you a solid base for the LLD interview that follows at most product companies.
Frequently Asked Questions
Go From OOP Basics to LLD Rounds
Prepflix's course connects OOP fundamentals to real low-level design problems, with mock interviews that probe exactly these follow-ups.
Explore the Prepflix Course →