LearnThatStack Ace your next interview

Object-Oriented Programming

Examples in Java unless a row names a language Checked Oct 2026 24 code snippets

The model

  • An object keeps its state private and changes it only through its own methods. So the class checks its rules in one place.
  • Callers depend on a contract, not a class. At runtime the object's own class picks the method, so a new type plugs in without editing the callers.
  • Inheritance gives reuse plus a promise that the subclass works wherever its parent does. For reuse alone, use composition.
  • The compiler picks an overload from the declared types. The runtime picks an override from the actual object.
  • In dependency inversion, high-level code owns the interface and the details implement it. Injection hands them in, so tests can pass fakes.
OrderSvcStripe SDK
directArrows point at a dependency. OrderSvc imports Stripe's SDK, so its tests need Stripe.
OrderSvcGatewayStripeFake
invertedEvery arrow points at Gateway, an interface OrderSvc owns. Stripe and a fake implement it.

Objects & the four pillars

what an object is, and the four ideas OOP rests on

The four pillars

Define encapsulationData stays private. Only its own methods change it.
Define abstractionCallers see what an object does, never how it does it.
Define inheritanceOne class builds on another and can stand in for it.
Define polymorphismThe same call runs each object's own version.

TipEncapsulation hides state and abstraction hides steps.

Classes, objects and constructors

Tell a class from an objectA class is the blueprint. Each new builds an object from it.
Name the kinds of constructorDefault, parameterized and copy. If you write none, Java adds one.
Release a file at a known timetry (var r = new FileReader(f))C# has using and Python has with.

TrapDon't release files in a finalizer. The collector may run it late or never. A C++ destructor runs at a known point, like the end of a scope.

Encapsulation & abstraction

hide state behind methods, and steps behind names

Hide state behind methods

Stop callers breaking a ruleprivate long balance;Change it only in methods that check the rule, like withdraw().
Recall Java's access levelsprivate is the class, default the package, protected adds subclasses.
Return a list without leaking itreturn List.copyOf(items);

TrapA getter and setter per field is a public field with extra typing. deposit(x) keeps the rule inside.

Abstraction or encapsulation

Tell abstraction from encapsulationAbstraction picks what to show. Encapsulation enforces the hiding.
Give a real-world example of eachA car pedal is abstraction. An ATM guarding your balance is encapsulation.
Build an abstractionInterfaces, abstract classes and well-named methods.

TipIf callers rely on details you meant to hide, the abstraction is wrong. If they change your state directly, the encapsulation is weak.

TrapA wrong abstraction costs more than none. Duplicated code is easier to fix than a bad contract.

Inheritance & interfaces

share code or share a contract, and where each stops

Inheritance shapes and the diamond

Name the shapes of inheritanceSingle, multilevel, hierarchical and multiple.
Explain the diamond problemTwo parents override one ancestor's method. Which wins?
Keep one copy of the ancestor in C++class B : virtual public A
See the order Python searchesD.__mro__C3 linearisation fixes one order.
Settle clashing default methods in JavaB.super.hello();The class must override it.

TipTake a parent's contract, not its code. Then the parent's next change can't break you.

Abstract class or interface

Share code and stateabstract class ShapeFields, constructors and finished methods. Extend only one.
Share only a contractinterface ShapeImplement many. Default methods are fine, instance fields aren't.
Explain why there's only one base classMerging two parents' fields and constructors has no right answer.

TipStart a public hierarchy from an interface. An abstract root spends each implementer's one superclass.

Polymorphism & overriding

which method runs, and who decides

Kinds of polymorphism

Run a subclass's version through a base typeSubtype polymorphism, picked at runtime.
Reuse one name with other parametersAd-hoc polymorphism, picked at compile time.
Write one class for every element typeclass Box<T> { T value; }Parametric polymorphism.
Accept any object with the right methodDuck typing, in dynamic languages like Python.

TipPolymorphism costs indirection. A call site no longer tells you which method runs.

Virtual functions in C++

Let a subclass replace a methodvirtual double area();Java methods are virtual already.
Force every subclass to implement itvirtual double area() = 0;It makes the class abstract.

TipA virtual call goes through a vtable, a per-class table of function pointers. That's one extra lookup per call.

TrapWithout a virtual destructor, deleting through a base pointer is undefined behaviour.

Overload or override

void feed(Animal a) { }void feed(Dog d) { }Animal pet = new Dog();feed(pet);runs feed(Animal), not feed(Dog)
Tell overloading from overridingOverloads change the parameters. Overrides keep the signature.
Write a valid overloadVary the parameters' number, types or order, not the return type.
Return a narrower type from an overrideDog copy() { return new Dog(); }Allowed. Callers skip the cast.
Catch an override that overrides nothing@Override

TrapChange a parameter type and you've written an overload. The base version still runs.

What a subclass can't override

Override a private methodYou can't. The subclass gets a new method the base never calls.
Override a static methodYou can't. A matching static hides it, and the declared type decides.
Stop subclasses overriding a methodpublic final void pay()C# uses sealed.
Call a static methodParent.greet();Name the class, never an instance.

TipConstructors are never inherited, so there's nothing to override.

TrapJava runs p.greet() even when p is null, because it ignores the instance. C# won't compile it.

Composition & coupling

has-a over is-a, and classes that know little of each other

Composition or inheritance

Tell composition from inheritanceA car has an engine. An electric car is a car.
Pick inheritanceOnly when the subtype is truly substitutable.
Count what composition costsHand-written delegation, more objects, deeper stack traces.
Add a few ready-made methods to a classclass Doc(JsonMixin, Base):A mixin, shown in Python.

TipYou can swap a composed part at runtime. A parent is fixed when you write the code.

TrapA subclass inherits the parent's whole public API, wanted or not. You can't take a method back.

Association, aggregation, composition

Spot associationObjects only use each other, like teacher and student.
Spot aggregationThe parts can outlive the whole, like a team's players.
Spot compositionThe whole owns its parts. Delete a house and its rooms go.

TipIn code, a composite builds its own parts. An aggregate receives them from outside.

Coupling and the Law of Demeter

order.customer().address().city();knows three classesorder.shippingCity();asks the order
Tell coupling from cohesionCoupling is how much modules depend on each other. Cohesion is how focused one module is.
Loosen two classes that know too muchMove each decision next to its data. Depend on a small interface.
State the Law of DemeterTalk to your own object, fields, parameters and what you create.

TrapApplied rigidly, the Law of Demeter fills classes with wrapper methods that only pass calls along.

Equality & hashing

when two objects count as the same

Reference or value equality

Check two variables hold the same objecta == bIn Java. Python uses a is b.
Check two objects hold the same contenta.equals(b)Python uses ==. A class needs its own equals for this.

TrapInterned strings make == pass in tests. It fails once a string comes from a file or the network.

equals and hashCode

Set<Point> seen = new HashSet<>();seen.add(new Point(1, 2));seen.contains(new Point(1, 2));false without hashCode
Override equalsOverride hashCode too, from the same fields.
Know which way the contract runsEqual objects must share a hash. Unequal ones may share one too.
List what equals must guaranteeReflexive, symmetric, transitive, consistent, and false for null.

TrapKeep mutable fields out of both. An object whose hash changes inside a set is lost.

Equality with subclasses, ids and sorting

Let subclasses pass the type checko instanceof Point pBreaks symmetry once a subclass adds a field.
Require the exact classgetClass() == o.getClass()Breaks with proxies and mocks.
Put an entity's id in equalsOnly if it exists at construction. Use a UUID or a business key.
Let equals and compareTo disagreeA HashSet keeps BigDecimal 1.0 and 1.00. A TreeSet keeps one.

TipMake the class final, or make equals final in the base and compare fixed fields.

TrapA database id is null before save, so new objects look equal. After save the hash changes.

Immutability & copies

objects that never change, and safe copies

Make a class immutable

Set every field onceprivate final int x;Assign it in the constructor. Make the class final too.
Store a mutable input safelythis.tags = List.copyOf(tags);Copy first, then validate.

Trapfinal fields alone are shallow. A final reference to a mutable list still lets callers change the list.

Shallow or deep copy

Make a shallow copy in Pythoncopy.copy(order)Shares the nested objects.
Make a deep copy in Pythoncopy.deepcopy(order)Copies everything the fields reach.
Deep-copy a Java objectclone() is shallow. Override it, or write a copy constructor.

TipShallow is enough when the nested objects are immutable, like strings.

TrapA shallow copy of a list of mutable items still shares the items. Copy them too.

Immutability for threads and map keys

Share an object across threads with no locksMake it deeply immutable and publish it safely.
Swap a new value into a shared fieldStill a race. Guard the field.
Pick a hash map key typeAn immutable one. Its hash never changes, so it can be cached.

TrapA key mutated after insert hashes to the wrong bucket. The entry stays, but nothing can find or remove it.

The SOLID principles

what each letter asks, and how to apply it

SOLID in one line each

Recall S, single responsibilityA class has one reason to change, so a change touches one file.
Recall O, open/closedAdd behaviour without editing code that already works.
Recall L, Liskov substitutionA subtype works anywhere its base type does, with no surprise throws.
Recall I, interface segregationSmall contracts beat one fat one, so nobody writes empty stubs.
Recall D, dependency inversionDepend on abstractions, so logic runs without a real database.

TipSOLID keeps each change in one place, and lets a test swap in fakes.

TrapApplied blindly, SOLID gives you a class per line of logic.

Single responsibility

Define a reason to changeA person or group who can ask for different behaviour.
Test a class for SRP quicklyDescribe it. If you need the word "and", it has two jobs.
Tell SRP from ISPSRP limits what a class does. ISP limits what clients depend on.

TipStop splitting when the reasons to change no longer overlap. Aim for cohesion, not small classes.

TrapSRP isn't "do one thing". It asks that a class have one reason to change.

Split a class with too many jobs

Split a UserRegistration classA validator, a hasher, a repository and a mailer, one per owning team.
Say what goes wrong months laterTests need a database and a clock. Teams collide in one file.

TipThe original class keeps only the order of the steps, and takes the four parts in its constructor.

Open/closed

Add a format without editing the exporterAn Exporter interface with one class per format.
Name patterns that support open/closedStrategy, Template Method, Observer, Factory and Decorator.
Balance open/closed with YAGNIStart simple. Add the extension point once the variation is real.

TipOpen for extension doesn't mean zero edits. The edit lands in the wiring, not the business logic.

TrapWhen providers differ in shape, the interface fills with optional methods. A switch is clearer then.

Liskov substitution

Check a subclass against its parentAccept at least as much, promise at least as much, keep its invariants.
Explain the square and rectangle problemA Square ties width to height. Code that sets them apart breaks.
Spot a violation in existing codeEmpty or throwing overrides, and instanceof checks before calls.
Fix a read-only list that extends a mutable oneSplit the base into a readable type and a mutable one that adds writes.

TrapThrowing UnsupportedOperationException breaks the contract. It moves a design error to runtime.

Interface segregation

Fix a 10-method interface when a class needs 2Pull out a 2-method interface, and let the wide one extend it.
Fix it when you don't own the interfaceAn adapter implements all ten and forwards the two you use.
Link small interfaces to coupling and cohesionSmall interfaces raise cohesion and cut what each client depends on.

TrapA fat interface forces stubs for methods a class never calls. Each stub turns a compile-time gap into a runtime failure.

Dependency inversion & DI

depend on interfaces, and hand objects their parts

Dependency inversion

Invert an OrderService that imports a vendor SDKThe order module owns a PaymentGateway. An adapter wraps the SDK.
Tell DIP from DIDIP is the rule and DI the usual tool. Injecting a concrete class isn't DIP.
Apply DIP without a containerTake interfaces in constructors. Build the real classes in main.

TrapAn interface that mirrors one class method for method inverts nothing. Make it narrower.

Inject a dependency

Say what dependency injection isAn object gets its collaborators from outside, not by building them.
Say what injection buys youFakes in tests, cheap swaps, and dependencies you can see.
Pass a required dependencyConstructor injection, the default. The object is never half-built.
Pass an optional dependencySetter injection. Nothing forces the call, so watch for nulls.
Pass a dependency that changes per callMethod injection. Pass it to the one method that needs it.

TipSources disagree on the third form. Some say method injection, others interface injection.

Containers and inversion of control

Say what a DI container doesIt builds the object graph, manages lifetimes and disposes objects.
Tell inversion of control from DIIoC means the framework calls your code. DI is one kind of it.

TipFor a small app, skip the container. Wire everything by hand in one startup function.

TrapA container turns wiring mistakes into startup or first-request failures, not compile errors.

When injection costs too much

Handle a stable, pure helperBuild it inline. A date formatter needs no test seam.
Read a constructor with eight dependenciesOne class doing eight jobs. Split it.
Know where heavy abstraction isn't worth itScripts, prototypes and hot paths.
Compare a service locator with DIThe class pulls from a global registry, so its needs stay hidden.

TrapA missing locator registration fails only when that path first runs. Tests must reset a global.

SOLID in real code

spot violations and refactor, for expert interviews

Spot a SOLID violation

Judge a class that fetches, computes and formatsIt breaks SRP, DIP and open/closed. Split fetch, compute and render.
Judge a method that switches over a type codeIt breaks open/closed and DIP. Use a class per variant or a handler map.
Find violations with metricsBig classes, type checks, fat interfaces, concrete dependencies.

TipKeep the switch when the variants are fixed and only the operations grow.

TrapA type switch is rarely alone. A new variant must reach every copy, and a missed one fails at runtime.

Refactor toward SOLID

Bring SOLID into a legacy codebaseAdd characterization tests, then refactor where code changes most.
Order the legacy workSRP first, then DIP at external boundaries, then the rest.
Refactor a god classPut each job behind an interface. A thin class calls them.
Settle two principles that clashBy context. SRP adds objects, and early open/closed breaks YAGNI.
Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 85 questions it links, not just the 9 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.