What you'll learn
Quick Answer
OOP interviews centre on the four pillars — encapsulation, abstraction, inheritance and polymorphism — plus overloading versus overriding, abstract classes versus interfaces, and why composition is usually preferred to inheritance. Strong answers pair each concept with the problem it solves and a short example, since almost every candidate can recite the definitions.
The Four Pillars, Answered Properly
Encapsulation — bundling data with the methods that operate on it, and restricting direct access. The reason, which candidates usually omit: it lets you change the internal representation without breaking callers, and it lets you validate.
class Account {
private double balance; // no direct access
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("must be positive");
balance += amount; // invariant protected
}
}Abstraction — exposing what something does while hiding how. You drive a car without knowing the engine internals. In code, an interface or abstract class defines the contract.
Expect the follow-up: difference between abstraction and encapsulation? Abstraction is about the design — deciding what to expose. Encapsulation is the mechanism — access modifiers enforcing it. Abstraction hides complexity; encapsulation hides data.
Inheritance — a class acquiring the fields and behaviour of another, modelling an is-a relationship. Note the caution: it creates tight coupling, and misuse is common.
Polymorphism — one interface, many implementations. Compile-time via overloading, runtime via overriding. The payoff is code that works with a base type and behaves correctly for any subtype:
for (Shape s : shapes) System.out.println(s.area()); // each computes its own
Overloading vs Overriding
Asked in almost every OOP round.
Overloading — same method name, different parameter lists, within one class. Resolved at compile time by the argument types.
int add(int a, int b)
double add(double a, double b)
int add(int a, int b, int c)Overriding — a subclass replacing a superclass method with the same signature. Resolved at runtime by the actual object type.
class Animal { void speak() { System.out.println("..."); } }
class Dog extends Animal { @Override void speak() { System.out.println("Woof"); } }
Animal a = new Dog();
a.speak(); // "Woof" — decided at runtime by the object, not the referenceThat last example is the crux, and interviewers often ask you to predict the output.
Common follow-ups:
- Can return type alone distinguish overloads? No — the compiler cannot choose based on return type.
- Can you override a static method? No. Statics belong to the class and are hidden, not overridden, so the reference type decides.
- Can a private or final method be overridden? No. Private methods are not visible to subclasses; final explicitly forbids it.
- Can a constructor be overloaded? Yes, and it is common. Overridden, no.
Abstract Class vs Interface
The difference: an abstract class can hold state and concrete methods and represents an is-a relationship; an interface defines a contract of behaviour, and a class can implement many.
abstract class Vehicle {
protected int wheels; // state
void start() { ... } // shared implementation
abstract void move(); // subclasses must supply
}
interface Chargeable {
void charge(); // contract only
}
class ElectricCar extends Vehicle implements Chargeable { ... }When to use which? Use an abstract class when subclasses share state or implementation and form a genuine hierarchy. Use an interface when unrelated classes need the same capability — a car and a phone are both chargeable without being related.
Modern caveat worth mentioning: Java 8 added default methods to interfaces, so they can now carry implementation. The distinction that remains is state — interfaces still cannot hold instance fields — and that a class may implement many interfaces but extend only one class.
Why does Java not allow multiple inheritance of classes? The diamond problem: if two parents define the same method, the compiler cannot choose. Interfaces avoid it because they historically carried no implementation, and where default methods now collide, Java forces the class to resolve it explicitly.
In Python, multiple inheritance is allowed, and conflicts are resolved by the method resolution order.
Composition Over Inheritance
A question that distinguishes candidates who have written real code.
What is composition? Building behaviour by holding other objects rather than inheriting from them — a has-a relationship instead of is-a.
// Inheritance: rigid
class Car extends Engine { } // a car is not an engine
// Composition: flexible
class Car {
private Engine engine; // a car HAS an engine
void start() { engine.start(); }
}Why is composition usually preferred? Inheritance is decided at compile time and cannot change; composition can be swapped at runtime. Inheritance exposes the parent's implementation to the child, so parent changes ripple downward. And deep hierarchies become difficult to reason about.
The classic illustration: a Square extends Rectangle looks correct mathematically but breaks, because setting width on a square must also change height, violating what callers expect of a rectangle. That is the Liskov substitution principle failing.
SOLID, in one line each — worth being able to name:
- 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 the parent is.
- Interface segregation — many small interfaces beat one large one.
- Dependency inversion — depend on abstractions, not concrete classes.
Language-Specific Follow-ups
Java. Difference between == and equals? == compares references for objects; equals compares content if overridden. If you override equals you must override hashCode, or hash-based collections will fail to find objects you stored.
What is the difference between String, StringBuilder and StringBuffer? String is immutable, so concatenation in a loop creates many objects. StringBuilder is mutable and not thread-safe; StringBuffer is mutable and synchronised.
Python. How is encapsulation done? By convention rather than enforcement — a single underscore signals internal use, and a double underscore triggers name mangling. Nothing is truly private, which reflects Python's "we are all consenting adults" philosophy.
What is the MRO? The method resolution order determines which parent's method is used under multiple inheritance, computed by the C3 linearisation and visible via ClassName.__mro__.
What are dunder methods? Special methods letting your class integrate with built-in behaviour — __init__, __str__, __len__, __eq__.
C++. What is a virtual function? One marked virtual so calls are resolved by the actual object type at runtime. Without it, calling through a base pointer runs the base version.
Why does a base class need a virtual destructor? Deleting a derived object through a base pointer with a non-virtual destructor is undefined behaviour and typically leaks the derived part. This is a frequent C++ interview question.
