Top 50 OOP Interview Questions and Answers (2026)
Object-oriented programming questions open almost every Java, C#, C++, Python, Kotlin and TypeScript round, and they decide most low-level design rounds ("design a parking lot"). They look easy, which is the trap. Anyone can recite the four pillars; the interviewer is listening for whether you know when inheritance is the wrong tool, why a singleton makes code hard to test, and what Liskov substitution actually forbids.
The 50 questions below escalate the way a real round does: the four pillars, objects in practice, SOLID, design principles, then creational, structural and behavioral patterns. After the 50 there is a bonus part on the low-level design round itself (questions 51-55) and a quick-fire round. Code is in Java and Python, the two languages these questions are asked in most; the ideas carry over.
Practice alongside this post. The same ground is drilled as timed multiple-choice in the object-oriented design question bank (56 graded questions, from the pillars to double dispatch); our coding challenges let you write the classes and run them against tests in the browser, and the free daily challenge gives you one question a day with no account needed. For the round where these ideas are applied end to end, read the low-level design interview guide.
Part 1: The Four Pillars (1-10)
1. What is object-oriented programming?
A way of structuring programs around objects: units that bundle state (fields) with the behaviour that operates on it (methods), and that interact by sending each other messages (calling methods) rather than by reaching into each other's data. The point is managing complexity: each object owns its own invariants, and the rest of the program depends on what it does, not how. Interviewers like an answer that names the benefit (change is localised) rather than only the mechanics.
2. Class vs object?
A class is a blueprint: it declares fields and methods. An object is an instance of a class, with its own copy of the instance fields, created at run time. Car is a class; the specific car with VIN 1HGCM... is an object. In some languages classes are objects too (Python, Ruby), which is where metaclasses come from.
3. What is encapsulation?
Keeping an object's state private and exposing only methods that keep it valid. The value is not "hiding fields," it is protecting invariants:
public final class Account {
private long balanceCents;
public void withdraw(long cents) {
if (cents <= 0) throw new IllegalArgumentException("amount must be positive");
if (cents > balanceCents) throw new IllegalStateException("insufficient funds");
balanceCents -= cents;
}
public long balanceCents() { return balanceCents; }
}
No caller can put the balance below zero, because no caller can touch balanceCents. A class with private fields and a public getter and setter for each one is not encapsulated in any useful sense; the follow-up is often "what is wrong with getters and setters everywhere?"
4. Abstraction vs encapsulation?
Abstraction is about the outside view: exposing what an object does and hiding how (a PaymentGateway interface with charge(), whatever the provider). Encapsulation is about the inside: bundling data with the methods that guard it and restricting direct access. Abstraction is a design decision about the interface; encapsulation is the mechanism that keeps implementation details from leaking. They work together, and a good answer says so.
5. What is inheritance, and what is it good for?
A class (subclass) derives from another (superclass), inheriting its fields and methods and optionally overriding them. It models an is-a relationship and lets you reuse and specialise behaviour. It is the tightest coupling in OOP: the subclass depends on the superclass's implementation details, so a change in the parent can break every child (the fragile base class problem). That is why the standard advice is to prefer composition (question 10) and keep hierarchies shallow.
6. What is polymorphism? Compile-time vs run-time?
One interface, many implementations. Code written against a supertype works with any subtype:
class Shape:
def area(self) -> float: ...
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return 3.14159 * self.r ** 2
class Square(Shape):
def __init__(self, s): self.s = s
def area(self): return self.s * self.s
total = sum(shape.area() for shape in [Circle(1), Square(2)])
Run-time (dynamic) polymorphism is method overriding: the implementation is chosen by the object's actual type when the call happens. Compile-time (static) polymorphism is method overloading and generics: the compiler picks the method from the declared argument types. Python has no overloading by signature; it gets the same effect with default arguments and duck typing.
7. How does dynamic dispatch work under the hood?
In C++, Java and C#, each class with virtual methods has a virtual method table (vtable): an array of pointers to the implementations for that class. Each object holds a pointer to its class's table. A virtual call loads the object's table pointer and calls through the slot for that method, so the subclass's override runs even when the variable's declared type is the superclass. The cost is an indirect call that can block inlining, which JIT compilers often recover by speculating on the common type. In C++, methods are not virtual unless you say so; in Java, every non-static, non-final, non-private method is.
8. Interface vs abstract class?
- An interface declares a contract. A class can implement many. Since Java 8 interfaces can have
default and static methods, but still no instance state.
- An abstract class can have fields, constructors and implemented methods alongside abstract ones. A class can extend only one (in Java and C#).
Use an interface for a capability that unrelated types share (Comparable, Closeable, PaymentMethod). Use an abstract class for a family of closely related types that share state and code, often as a template (question 46). When in doubt, start with an interface: it is easier to add an abstract base class later than to remove one.
9. What is the diamond problem, and how do languages handle it?
If D inherits from both B and C, and both inherit from A and override the same method, which version does D get, and how many copies of A's state does it have?
- Java and C# avoid it for state by allowing only single inheritance of classes. With interface default methods, if two interfaces provide the same default, the class must override it explicitly (and can call
B.super.method()).
- C++ allows multiple inheritance;
virtual inheritance gives D one shared A subobject, and ambiguous calls must be qualified.
- Python computes a method resolution order (MRO) with the C3 linearisation, so
super() follows a predictable chain; D.__mro__ shows it.
10. Composition vs inheritance: why "favour composition"?
Composition builds behaviour by holding references to other objects (has-a) and delegating to them; inheritance gets it by being a subtype (is-a). Composition is preferred because it is looser coupling: you depend only on the collaborator's public interface, you can swap implementations at run time, and you avoid deep hierarchies where every combination of features needs its own subclass (LoggingRetryingCachingClient).
Inheritance still wins when there is a genuine is-a relationship with substitutable behaviour, when a framework is designed around extension points, and for small, stable hierarchies (a sealed set of AST node types). The answer interviewers want is "composition by default, inheritance when Liskov holds and the hierarchy is stable."
Part 2: Objects in Practice (11-18)
11. Method overloading vs overriding?
Overloading: same name, different parameter lists, in the same class; chosen at compile time from the argument types. Overriding: a subclass provides its own implementation of a method with the same signature as the parent's; chosen at run time from the object's type. The trap question: overloads are resolved on the declared type of the arguments, so passing an Object variable that holds a String calls the Object overload.
12. What do access modifiers control?
Who can see a member. In Java: private (the class only), package-private (default, same package), protected (package plus subclasses), public (everyone). C# adds internal (same assembly). Python has no enforcement: a single underscore is a convention for "internal," and a double underscore triggers name mangling to avoid accidental clashes in subclasses. The design principle: start everything as private and widen deliberately.
13. Static vs instance members?
Instance fields and methods belong to each object. Static ones belong to the class itself and are shared by every instance (a counter, a factory method, constants). Static methods cannot use this and cannot be overridden polymorphically (in Java they are hidden, not overridden). Heavy use of static mutable state is global state in disguise: it hurts testability and thread safety.
14. What is the equals and hashCode contract?
If two objects are equal by equals, they must have the same hashCode. (Unequal objects may share a hash code; that is just a collision.) equals must also be reflexive, symmetric, transitive and consistent. Break the contract and hash-based collections fail silently: you put an object in a HashSet, then contains on an equal object returns false because it looks in a different bucket. Two follow-ups: compute both from the same fields, and do not mutate those fields while the object is in a hash-based collection. Python's equivalent is __eq__ and __hash__; defining __eq__ alone makes instances unhashable.
15. What is a value object, and why make it immutable?
A value object is defined by its values, not an identity: a Money(100, "USD") equals any other Money(100, "USD"). Make it immutable: all fields final, no setters, operations return new instances.
public record Money(long cents, String currency) {
public Money plus(Money other) {
if (!currency.equals(other.currency)) throw new IllegalArgumentException("currency mismatch");
return new Money(cents + other.cents, currency);
}
}
Immutable objects are thread-safe without locks, safe to share and cache, safe as map keys, and cannot be put into an invalid state after construction. Java records, Kotlin data classes and Python frozen dataclasses make them cheap to write.
16. Shallow copy vs deep copy?
A shallow copy duplicates the object but shares the objects it references; a deep copy recursively duplicates those too. Mutating a nested list through a shallow copy changes the original. In Python, copy.copy is shallow and copy.deepcopy is deep; in Java, clone() is shallow by default and generally avoided in favour of copy constructors. Immutability (question 15) makes the question disappear: shared immutable parts are safe.
17. What is a constructor, and what should one do?
A special method that initialises a new object so that it starts in a valid state. It should validate arguments and establish invariants, and avoid real work: no network calls, no starting threads, and no calling overridable methods (in Java, an overridden method called from a superclass constructor runs before the subclass's fields are initialised). When construction is complex or has many optional parameters, use a factory method or a builder (questions 32 and 34).
18. What is the difference between association, aggregation and composition (in UML)?
All three are has-a relationships of increasing strength. Association: one object uses or knows about another (a Teacher and a Student). Aggregation: a whole-part relationship where the parts can exist independently (a Team has Players; delete the team, the players remain). Composition: the parts' lifetime is owned by the whole (a House and its Rooms; delete the house, the rooms go too). This comes up when you draw class diagrams in a design round.
Part 3: SOLID (19-25)
19. Single Responsibility Principle.
A class should have one reason to change, meaning it should serve one actor or concern. An Invoice class that calculates totals, renders a PDF and saves itself to the database will change when the tax rules change, when the PDF layout changes and when the schema changes. Split it into Invoice (the domain), InvoicePdfRenderer and InvoiceRepository. The tell in an interview is a class name with "And" or "Manager" in it, or a method list that touches several unrelated dependencies.
20. Open/Closed Principle.
Software entities should be open for extension, closed for modification: you add behaviour by adding code, not by editing working code. The usual mechanism is polymorphism: instead of a switch on payment type that grows with every new provider, define a PaymentMethod interface and add a class per provider. The caveat worth saying: you cannot be closed against every change, so apply it at the points you expect to vary.
21. Liskov Substitution Principle.
Objects of a subtype must be usable anywhere the supertype is expected without breaking correctness. A subclass may not strengthen preconditions, weaken postconditions or break the parent's invariants.
The classic violation is Square extends Rectangle: a caller that sets width to 5 and height to 4 expects an area of 20, but a square that keeps its sides equal gives 16. Other tells: a subclass that throws UnsupportedOperationException for an inherited method, or callers that check instanceof before using an object. The fix is usually a different hierarchy, not a cleverer override.
22. Interface Segregation Principle.
Clients should not be forced to depend on methods they do not use. A fat Machine interface with print(), scan() and fax() forces a simple printer to stub out two methods (and often violate LSP while doing it). Split it into Printer, Scanner and Fax, and let a multifunction device implement all three. Small, role-based interfaces also make mocking in tests trivial.
23. Dependency Inversion Principle.
High-level modules should not depend on low-level modules; both should depend on abstractions, and the abstractions should be owned by the high-level side. An OrderService should depend on an OrderRepository interface it defines, not on a PostgresOrderDao; the Postgres class implements the interface. The business logic then does not change when the storage does, and tests can pass an in-memory implementation.
24. Dependency Inversion vs Dependency Injection vs Inversion of Control?
- Dependency Inversion is a design principle about the direction of dependencies (question 23).
- Dependency Injection is a technique: an object receives its collaborators from outside (constructor, setter or method parameters) instead of creating them.
- Inversion of Control is the broader idea that a framework calls your code rather than your code calling the framework; DI containers (Spring, .NET's built-in container, Guice) are one form of it.
You can do DI with no container at all: passing dependencies into constructors is DI. Constructor injection is preferred because dependencies are explicit, required and can be final.
25. Service Locator vs Dependency Injection?
A service locator is a registry objects call to fetch their dependencies (Services.get(Mailer.class)). It decouples creation, but it hides dependencies: you cannot see from a constructor what a class needs, tests must configure a global registry, and a missing registration fails at run time deep inside a call. Dependency injection makes dependencies explicit in the signature. Most teams treat service locator as an anti-pattern except at the composition root or in frameworks that need it.
Part 4: Design Principles (26-30)
26. Cohesion vs coupling?
Cohesion is how closely the responsibilities inside one module belong together; coupling is how much one module depends on the internals of others. The goal is high cohesion, low coupling: each class does one coherent job, and changes to it do not ripple through the codebase. Most OOP principles are ways of moving toward that pair.
27. What is the Law of Demeter?
"Talk only to your immediate friends." A method should call methods on itself, its fields, its parameters and objects it creates, not on objects returned by those calls. order.getCustomer().getAddress().getZipCode() couples the caller to the structure of three classes; if Customer changes how it stores addresses, the caller breaks. Prefer order.shippingZipCode(), letting each object answer for its own data. It is a guideline: fluent builders and streams chain calls on purpose and do not violate the spirit of it.
28. What is "Tell, Don't Ask"?
Rather than asking an object for its data and making decisions about it outside, tell the object what to do and let it decide. Instead of if (account.getBalance() >= amount) account.setBalance(account.getBalance() - amount), call account.withdraw(amount). The logic moves to where the data lives, which is encapsulation (question 3) applied to behaviour.
29. What is a God Object, and how do you break one up?
A class that knows too much and does too much: thousands of lines, dozens of dependencies, used by everything (ApplicationManager, Utils, OrderService with 80 methods). It violates SRP and is the place where every merge conflict happens. Break it up by finding clusters of fields and methods that are used together, extracting each cluster into its own class, and moving behaviour next to the data it uses. Do it incrementally, behind tests.
30. What are DRY, KISS and YAGNI, and when do they conflict?
DRY (don't repeat yourself): every piece of knowledge has one authoritative representation. KISS (keep it simple): prefer the simplest design that works. YAGNI (you aren't gonna need it): do not build for requirements you do not have. They conflict when removing duplication requires an abstraction that is harder to understand than the duplication was. Two similar-looking pieces of code that change for different reasons are not real duplication; merging them couples things that should evolve separately. The usual heuristic is to tolerate duplication until the third occurrence makes the shared concept clear.
Part 5: Creational Patterns (31-35)
31. What is the Singleton pattern, and what is wrong with it?
A class that guarantees one instance and provides global access to it. Legitimate uses are rare: a process-wide resource that truly must be unique. The problems:
- It is global mutable state with a nicer name.
- It hides dependencies (code calls
Config.getInstance() instead of receiving a config), which makes testing hard: you cannot substitute a fake.
- Lazy initialisation is a concurrency trap (double-checked locking needs
volatile in Java); an enum singleton or a static holder class avoids it.
- "One per process" stops being true with multiple class loaders, processes or test runs.
The modern answer: let the DI container create one instance and inject it. You get the "only one" without the global access.
32. Factory Method vs Abstract Factory?
Factory Method: a method that creates objects, letting subclasses or configuration decide the concrete type, so callers depend only on the interface (Notifier.create(channel) returning an EmailNotifier or SmsNotifier). Abstract Factory: an object that creates families of related objects that must be used together (UiFactory with createButton() and createCheckbox(), implemented by MacUiFactory and WindowsUiFactory, so you never mix a Mac button with a Windows checkbox). Factory method varies one product; abstract factory varies a consistent set.
33. When would you use a factory instead of a constructor?
When the caller should not know the concrete class, when the choice of class depends on input or configuration, when creation is complex enough to deserve a name (Money.ofCents(250) reads better than an overloaded constructor), or when you want to return a cached instance or a subtype. Static factory methods can also have descriptive names, which constructors cannot.
34. What problem does the Builder pattern solve?
Constructing an object with many parameters, most of them optional, without a "telescoping" set of constructors or a constructor call full of nulls and booleans no reader can decode:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/orders"))
.timeout(Duration.ofSeconds(5))
.header("Accept", "application/json")
.GET()
.build();
The builder accumulates settings and build() validates them and returns an (often immutable) object. In Python and Kotlin, keyword and default arguments cover most of what a builder does, so the pattern is mostly a Java and C# idiom.
35. What is the Prototype pattern?
Create new objects by copying an existing instance instead of constructing from scratch: useful when construction is expensive or when you need many variations of a preconfigured object. The catch is the copy semantics: you must decide between shallow and deep copies (question 16). JavaScript's object model is prototype-based at the language level, which is a different (if related) idea.
Part 6: Structural Patterns (36-41)
36. What does the Adapter pattern do?
It converts the interface of an existing class into the interface a client expects. You have a PaymentProcessor interface; a third-party SDK exposes makePayment(amountInDollars). An adapter implements PaymentProcessor and translates calls to the SDK. It is how you integrate code you cannot change, and how you keep a vendor's API from spreading through your codebase.
37. What is the Decorator pattern? How is it different from inheritance?
A decorator wraps an object that implements an interface and implements the same interface, adding behaviour before or after delegating:
interface DataSource { void write(byte[] data); }
class CompressingDataSource implements DataSource {
private final DataSource inner;
CompressingDataSource(DataSource inner) { this.inner = inner; }
public void write(byte[] data) { inner.write(compress(data)); }
}
DataSource ds = new EncryptingDataSource(new CompressingDataSource(new FileDataSource("out.bin")));
Unlike subclassing, decorators compose at run time and avoid a subclass for every combination of features. Java's I/O streams (BufferedInputStream wrapping a FileInputStream) are the textbook example; Python's function decorators are the same idea applied to functions.
38. What is a Facade?
A simplified interface in front of a complex subsystem. A VideoConverter.convert(file, "mp4") facade hides the codec, bitrate and container classes behind one call. It reduces coupling between clients and the subsystem's internals without preventing advanced clients from using the subsystem directly. The difference from Adapter: Adapter makes an existing interface fit an expected one; Facade defines a new, simpler one.
39. What is the Proxy pattern, and what kinds are there?
A proxy implements the same interface as a real object and controls access to it. Kinds: virtual proxy (lazy creation of an expensive object), protection proxy (access control checks), remote proxy (a local stand-in for an object in another process, like an RPC stub), and caching or logging proxies. ORMs use proxies for lazy-loaded relationships, and frameworks like Spring use them to add transactions around your methods, which is why calling a transactional method from inside the same class can bypass the proxy.
40. What is the Composite pattern?
It lets clients treat individual objects and groups of objects uniformly through one interface. A FileSystemNode interface with size() is implemented by File (returns its size) and Directory (returns the sum of its children's sizes, which may themselves be directories). Trees of UI components, organisation charts and expression trees use it. The trade-off is that the shared interface may expose operations (like addChild) that do not make sense on leaves.
41. Bridge and Flyweight: what problems do they solve?
Bridge separates an abstraction from its implementation so the two can vary independently: instead of CircleOnScreen, CircleOnPrinter, SquareOnScreen and so on, a Shape holds a reference to a Renderer. It prevents a class explosion when two dimensions of variation multiply.
Flyweight shares common state among many fine-grained objects to save memory: a text editor stores each glyph's font data once (intrinsic, shared) and keeps only the position per character (extrinsic, supplied by the caller). Java's Integer.valueOf cache for small values is a flyweight.
Part 7: Behavioral Patterns (42-50)
42. What is the Strategy pattern?
Define a family of interchangeable algorithms behind one interface and choose one at run time. A PricingStrategy with RegularPricing, MemberPricing and SalePricing; the checkout holds a strategy and calls it. It replaces conditionals with polymorphism and is the most direct application of the Open/Closed Principle. In languages with first-class functions, a strategy is often just a function passed as an argument.
43. What is the Observer pattern, and what are its pitfalls?
A subject keeps a list of observers and notifies them when its state changes, so they stay in sync without the subject knowing their concrete types. Event listeners, UI data binding and pub/sub are all forms of it.
Pitfalls: memory leaks from observers that never unsubscribe (the subject keeps them alive), unpredictable notification order, cascades where one update triggers others, and exceptions in one observer breaking the notification loop. Across processes it becomes a message broker with delivery guarantees to think about.
44. What does the Command pattern give you?
It turns a request into an object with an execute() method (and often undo()). Because requests are objects, you can queue them, log them, retry them, schedule them, and implement undo/redo by keeping a history stack. Text editors, job queues and transactional scripts use it. The CQRS "command" is a related idea: an object describing an intent to change state.
45. State vs Strategy: they look the same. What is the difference?
Structurally they are nearly identical: a context delegates to an object behind an interface. The difference is who changes the object and why. With Strategy, the client chooses the algorithm and it usually stays fixed for the operation. With State, the object represents the context's current state, and the states themselves trigger transitions (a Draft document's publish() moves it to InReview). Use State to replace a large switch on a status field that appears in many methods.
46. What is the Template Method pattern?
A base class defines the skeleton of an algorithm in a method, calling abstract or overridable steps that subclasses fill in. A DataImporter with run() calling open(), parse(), validate() and save(), where CsvImporter and JsonImporter override parse(). It is inheritance-based reuse, so it carries the coupling costs of question 5; the composition alternative is to pass the varying steps in as strategies.
47. What is Chain of Responsibility?
A request passes along a chain of handlers, and each one either handles it or passes it to the next. HTTP middleware is the everyday example: authentication, rate limiting, logging and the route handler, each deciding whether to continue. It decouples senders from receivers and lets you reorder or add handlers without changing the others. The risk is a request that falls off the end unhandled, so chains usually end in a default handler.
48. What is the Visitor pattern, and what is double dispatch?
Visitor lets you add operations to a fixed set of element classes without modifying them. Each element has accept(visitor), which calls visitor.visit(this); the visitor has one visit overload per element type. That is double dispatch: the method that runs depends on two run-time types, the element's (through accept) and the visitor's (through visit). Compilers use it to run many passes (type checking, code generation) over the same syntax tree.
The trade-off: adding a new operation is easy (a new visitor), adding a new element type is hard (every visitor must change). Languages with pattern matching over sealed types (Kotlin, modern Java, Scala, Rust) often replace it with a switch.
49. What is the Null Object pattern?
Instead of returning null and forcing every caller to check, return an object that implements the interface with do-nothing behaviour: a NoOpLogger, an empty Discount that subtracts zero. It removes null checks and the crashes from forgetting one. It is wrong when absence is meaningful and the caller must handle it, in which case an Optional or a nullable type that the compiler checks is better.
50. What is the Repository pattern?
A repository presents a collection-like interface for domain objects (findById, save, findOverdueInvoices) and hides how they are stored. The domain layer depends on the repository interface (Dependency Inversion, question 23); the implementation uses SQL, an ORM or an API. Benefits: business logic that does not mention the database, and tests that use an in-memory implementation. The trap is a generic repository that just re-exposes every query, which leaks storage concerns back into the domain.
Bonus Part 8: The Low-Level Design Round (51-55)
Low-level design (LLD), or object-oriented design, rounds hand you a small system ("design a parking lot," "design a vending machine") and 45 minutes. The interviewer is grading your process and your judgement more than the final class list.
51. How do you approach "design a parking lot"?
- Clarify requirements (5 minutes): vehicle types, spot sizes, multiple floors, entry and exit gates, payment, what happens when full.
- Name the core objects from the nouns:
ParkingLot, Floor, Spot (with a size), Vehicle, Ticket, Gate, PaymentProcessor.
- Assign responsibilities: who finds a free spot (a
SpotAllocator strategy, so the policy can change), who issues and closes tickets, who computes the fee (a PricingStrategy).
- Write the key interfaces and one or two classes, not all of them.
- Walk a scenario through the design: a car enters, parks, pays, leaves.
- Discuss trade-offs and extensions: concurrency at the gates (two cars assigned the same spot), adding electric charging spots, persistence.
52. How do you decide what becomes a class?
Start with the nouns in the requirements, then keep the ones that have both state and behaviour or that represent a concept the business talks about. A noun with only data and no rules is often a value object or a field. A verb that varies by policy (pricing, allocation, notification) is a strategy. Avoid "manager" classes that do everything; if you cannot describe a class's responsibility in one sentence, split it.
53. Refactor this: a calculatePay method with a switch on employee type.
double calculatePay(Employee e) {
switch (e.type) {
case SALARIED: return e.salary / 12;
case HOURLY: return e.hours * e.rate;
case CONTRACT: return e.invoiceTotal;
default: throw new IllegalArgumentException("unknown type " + e.type);
}
}
Every new employee type means editing this method and every other method with the same switch (an Open/Closed violation). Replace the type code with polymorphism: a PayPolicy interface with SalariedPay, HourlyPay and ContractPay, and an Employee that holds its policy. Composition rather than subclassing Employee means a person can change pay type without becoming a different object.
54. How do you handle concurrency in an LLD answer?
Name the shared mutable state and protect it at the narrowest point. In the parking lot, the spot assignment is the race: two gates must not assign the same spot. Options: a lock around allocation, an atomic compare-and-set on the spot's status, or a single allocator that serialises requests through a queue. Mention that a real system would enforce it in the database (a unique constraint or a conditional update), because an in-memory lock only protects one process.
55. Is OOP still relevant, or is functional programming better?
Both are tools, and modern languages mix them: Java has records, sealed types and lambdas; Kotlin, C#, TypeScript and Python are multi-paradigm. The strong answer: use objects to model entities with identity and lifecycles and to define boundaries (interfaces, dependency inversion), and use functional techniques (immutable values, pure functions, higher-order functions) for data transformation and logic. Many patterns in this post shrink to a function in a functional style, which is a sign they were about the idea, not the class diagram.
Quick-Fire: OOP Interview Questions and Answers
this vs super - this refers to the current object; super calls the parent class's implementation or constructor.
- Can you override a static method? - No; in Java a static method with the same signature in a subclass hides the parent's, and the call is resolved on the declared type.
final in Java - a final class cannot be extended, a final method cannot be overridden, a final variable cannot be reassigned (the object it points to can still change).
- Abstract method - declared without a body; any concrete subclass must implement it.
- Covariant return type - an overriding method may return a subtype of the parent method's return type.
- Mixin - a class that provides methods to be combined into other classes without being a parent in the is-a sense; Python uses multiple inheritance for it, Kotlin and Scala use interfaces or traits.
- Duck typing - "if it walks like a duck": Python checks for the method at call time rather than a declared type;
typing.Protocol adds a static check for the same idea.
- Object identity vs equality - identity is "the same object" (
== on references in Java, is in Python); equality is "equivalent value" (equals, == in Python).
- Encapsulation in Python - by convention (
_internal), with name mangling for __private to avoid subclass clashes, and @property to add validation without changing the call site.
- Anemic domain model - objects that are only data with getters and setters, with all logic in services; the opposite of tell-don't-ask.
- Interface default method conflict - if two interfaces give the same default method, the implementing class must override it and pick one explicitly.
- SOLID in one line each - one reason to change; extend without editing; subtypes keep the contract; small interfaces; depend on abstractions.
How to Practice for an OOP Interview
- Write the code, not just the definitions. Implement Strategy, Observer, Decorator and Builder once each in your interview language. Twenty minutes per pattern is enough to make them concrete.
- Practise three LLD prompts end to end, out loud, with a timer: a parking lot, an elevator system and a library or vending machine. Follow the steps in question 51 each time.
- For every principle, have one violation ready. Square and Rectangle for LSP, a fat interface for ISP, a
switch on type codes for OCP. Interviewers often show you the violation and ask what is wrong.
- Say the cost of every pattern. "Decorator avoids a subclass per feature combination, at the cost of more small objects and harder debugging" is the sentence that sounds senior.
- Drill the recall questions under time pressure so the pillars and SOLID take seconds and the time goes on design.
Keep going
- Drill it under pressure: the object-oriented design question bank covers the pillars, SOLID and the patterns as timed multiple-choice, and the free daily challenge gives you one question a day with no account.
- Apply it: the low-level design interview guide walks through the full LLD round, and our coding challenges run your classes against real test cases in the browser.
- Keep a reference open: the interview patterns cheat sheet for the algorithmic half of the loop, and the REST API design cheat sheet for the interface-design questions that follow LLD.
- Round out the loop: Top 50 Java, C#, Python, Kotlin and Android and TypeScript interview questions, plus Top 30 system design interview questions for the high-level round.
- Practise the follow-ups: the chat-based AI mock interview asks the "and what if a second gate assigns the same spot?" questions a real design round runs on.
#oop #objectorienteddesign #designpatterns #solid #interviews #lowleveldesign #career