Menu
CoddyTech

OOPs Interview Questions and Answers

OOPs questions on the four pillars, constructors, inheritance, interfaces, SOLID and design patterns, answered in Java with Python and C++ where they differ. Try the output questions before reading the answer.

82 questions16 output quizzesRunnable code checked on Java 24 and Python 3.11By Kevin Spektor, Co-founder & CTO

OOPs interview questions for freshers

The basics placement rounds start with: what OOP is, classes and objects, access modifiers, two output questions.

What is OOPs (object-oriented programming)?

Fresheroops basics

Object-oriented programming (OOP) structures a program around objects: bundles of data (fields) and the methods that work on that data. A class describes the shape; each object is one instance with its own state.

Java
public class Main {
    public static void main(String[] args) {
        Counter a = new Counter("clicks");
        Counter b = new Counter("views");
        a.increment();
        a.increment();
        b.increment();
        System.out.println(a + ", " + b);
    }
}

class Counter {
    private final String name;
    private int value;

    Counter(String name) { this.name = name; }

    void increment() { value++; }

    @Override
    public String toString() { return name + "=" + value; }
}

One class, two objects counting separately: it prints clicks=2, views=1.

What is the difference between object-oriented and procedural programming?

Fresheroops basics

Procedural code is functions that operate on data passed to them; object-oriented code keeps data and the functions that change it together in objects. The difference shows when you add a new kind of thing: procedural code edits every function that switches on the type, OOP adds one class.

What is the difference between a class and an object?

Fresheroops basics

A class is a blueprint that declares fields and methods and holds no per-object data (only its static fields). An object is an instance created from it at runtime (with new in Java), with its own copy of the instance fields. Syntax in Java classes.

What are the four pillars of OOPs?

Fresheroops basicspillars

Encapsulation, abstraction, inheritance and polymorphism.

  1. Encapsulation: private fields changed only through methods that enforce the rules.
  2. Abstraction: expose what an object does, hide how.
  3. Inheritance: a class extends another in an is-a relation (class Dog extends Animal).
  4. Polymorphism: one call, behavior chosen by the object (shape.area()).

What are the advantages and disadvantages of OOP?

Fresheroops basics

OOP makes large programs easier to change because each class owns its data and rules, but it adds ceremony small programs do not need. Gains: modularity, reuse, new types without editing callers, and testing with fakes. Costs: more indirection, fragile deep inheritance trees, and shared mutable objects that complicate concurrency.

Is Java a pure object-oriented language?

Fresheroops basicsjava vs python

No. Java's eight primitive types (int, double, boolean and so on) are not objects, and static members belong to no object. Wrapper classes (Integer) and autoboxing bridge the gap. Smalltalk is pure OOP, and Python is close, since even integers and functions are objects:

Python
print((5).bit_length(), type(len).__name__, isinstance(len, object))

It prints 3 builtin_function_or_method True.

What is the difference between a method and a function?

Fresheroops basics

A method is a function that belongs to a class and runs in the context of an object (or of the class, if static); a function stands alone. In Java every function is a method. Python has both: len(items) is a function, items.append(3) a method that receives items as self.

What are access modifiers in OOPs?

Fresheraccess control

Access modifiers decide which code may use a class member. Java has four: private (the class only), package-private with no keyword (the package), protected (the package plus subclasses elsewhere) and public (everywhere). Keep fields private. Python has only conventions (_name) and name mangling (__name). Details in Java access modifiers.

What does this print: a static counter shared by three objects?

Fresherstaticoops basics
Java
public class Main {
    public static void main(String[] args) {
        Student first = new Student();
        new Student();
        Student third = new Student();
        System.out.println(Student.count + " " + first.id + " " + third.id);
    }
}

class Student {
    static int count = 0;
    int id;

    Student() {
        count++;
        id = count;
    }
}

Predict the output

count is static, so the three objects share one copy and it ends at 3. id is an instance field, so each object keeps the value count had when it was built: 1 for the first, 3 for the third. A static method could read count but not id, because it has no this.

What does this print: == vs equals() on strings?

Fresherequality
Java
public class Main {
    public static void main(String[] args) {
        String a = "hi";
        String b = "hi";
        String c = new String("hi");
        System.out.println((a == b) + " " + (a == c) + " " + a.equals(c));
    }
}

Predict the output

== compares references, equals() compares content. String literals are interned, so a and b are the same pooled object and a == b is true. new String("hi") always creates a new object, so a == c is false while a.equals(c) is true. Compare objects with equals(); use == for primitives, enums and deliberate identity checks.

The four pillars of OOPs: encapsulation, abstraction, inheritance, polymorphism

One question per pillar with runnable code, plus the comparisons that test whether you understand them.

What is encapsulation? Explain with an example.

Fresherpillarsencapsulation

Encapsulation bundles data with the methods that use it and hides the data behind them, so the object enforces its own rules: private fields, methods that validate every change.

Java
public class Main {
    public static void main(String[] args) {
        BankAccount acc = new BankAccount(100);
        acc.withdraw(30);
        System.out.println(acc.getBalance());
        try {
            acc.withdraw(500);
        } catch (IllegalArgumentException e) {
            System.out.println("Refused: " + e.getMessage());
        }
        System.out.println(acc.getBalance());
    }
}

class BankAccount {
    private int balance;

    BankAccount(int opening) { balance = opening; }

    int getBalance() { return balance; }

    void withdraw(int amount) {
        if (amount <= 0 || amount > balance) {
            throw new IllegalArgumentException("invalid amount " + amount);
        }
        balance -= amount;
    }
}

It prints 70, then Refused: invalid amount 500, then 70. A public setBalance() would let callers skip the rule, so getters and setters for everything are not encapsulation.

What is the difference between abstraction and encapsulation?

Fresherpillarsabstraction

Abstraction hides complexity by showing only what an object does; encapsulation hides state by controlling who reads and changes it. Abstraction is the contract (interfaces, abstract classes), encapsulation the implementation (private fields, access modifiers). List lets you add and get without knowing it is an array; ArrayList keeps that array private.

What is inheritance, and what types of inheritance does Java support?

Fresherpillarsinheritance

Inheritance lets a subclass reuse and extend the fields and methods of a superclass, modelling an is-a relation: a Truck is a Vehicle. A Java class extends one class but can implement many interfaces, which since Java 8 can carry default methods. Python and C++ allow several superclasses.

Java
public class Main {
    public static void main(String[] args) {
        System.out.println(new Truck("MH12", 8).info());
    }
}

class Vehicle {
    protected final String plate;

    Vehicle(String plate) { this.plate = plate; }

    String info() { return "Vehicle " + plate; }
}

class Truck extends Vehicle {
    private final int tons;

    Truck(String plate, int tons) {
        super(plate);
        this.tons = tons;
    }

    @Override
    String info() { return super.info() + ", " + tons + " t"; }
}

It prints Vehicle MH12, 8 t. More in Java inheritance.

What is polymorphism? What are its types?

Fresherpillarspolymorphism

Polymorphism means one interface, many implementations: the same call does different work depending on the object. Compile-time polymorphism is overloading, picked by the compiler from the declared argument types; runtime polymorphism is overriding, picked from the object's actual class.

Java
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Shape> shapes = List.of(new Circle(1), new Square(2));
        for (Shape s : shapes) {
            System.out.printf("%s %.2f%n", s.getClass().getSimpleName(), s.area());
        }
    }
}

interface Shape { double area(); }

record Circle(double r) implements Shape {
    public double area() { return Math.PI * r * r; }
}

record Square(double side) implements Shape {
    public double area() { return side * side; }
}

It prints Circle 3.14 and Square 4.00; the loop never checks the type. See Java polymorphism.

What is the difference between static binding and dynamic binding?

Experiencedpolymorphism

Static (early) binding resolves a call at compile time; dynamic (late) binding resolves it at runtime from the object's actual class. In Java, overloads, static and private methods, constructors and field access are bound statically, so fields are not polymorphic. Overridable instance methods are dynamic: Animal a = new Dog(); a.sound(); runs Dog.sound().

What does this print: a getter that returns a private final list?

Experiencedencapsulation
Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Team team = new Team();
        team.add("Asha");
        team.getMembers().add("Intruder");
        System.out.println(team.getMembers().size());
    }
}

class Team {
    private final List<String> members = new ArrayList<>();

    void add(String name) { members.add(name); }

    List<String> getMembers() { return members; }
}

Predict the output

final fixes the reference, not the list. getMembers() hands out the internal list itself, so the caller adds "Intruder" straight into the team and the size is 2, even though the field is private final. Return a copy instead:

List<String> getMembers() { return List.copyOf(members); }

Now the caller's add throws UnsupportedOperationException.

Classes, objects and constructors interview questions

Constructors, initialization order, this and super, static members, copying and cleanup.

What is a constructor, and how is it different from a method?

Fresherconstructors

A constructor runs once when an object is created and puts it into a valid starting state. It has the class's name, no return type (not even void), and is not inherited. The trap: Java adds a no-argument default constructor only when you write none, so after adding Point(int x, int y), new Point() stops compiling. See Java constructors.

What is constructor overloading and constructor chaining?

Fresherconstructors

Constructor overloading is several constructors with different parameter lists. Chaining is one constructor calling another with this(...), or the parent's with super(...), so the setup lives in one place.

Java
public class Main {
    public static void main(String[] args) {
        System.out.println(new Pizza() + " " + new Pizza("large") + " " + new Pizza("small", "thick"));
    }
}

class Pizza {
    final String size;
    final String crust;

    Pizza() { this("medium"); }

    Pizza(String size) { this(size, "thin"); }

    Pizza(String size, String crust) {
        this.size = size;
        this.crust = crust;
    }

    @Override
    public String toString() { return size + "/" + crust; }
}

It prints medium/thin large/thin small/thick. Before Java 25 the call must be the first statement (Java 22 to 24 only previewed the change); Java 25 allows code before it that does not read the object being built, see JEP 513.

What are the types of constructors? Does Python have a constructor?

Fresherconstructorsjava vs python

Default (no arguments), parameterized and copy (built from another object of the same class). Java and C++ add a default constructor only if you write none. C++ generates a memberwise copy constructor unless you declare your own, and deletes it if you declare a move operation; Java has none, so you write Point(Point other). Python has one __init__, which strictly is the initializer: __new__ creates the object.

What does this print: constructors in a three-level class hierarchy?

Fresherconstructorsinheritance
Java
public class Main {
    public static void main(String[] args) {
        new C();
    }
}

class A { A() { System.out.print("A "); } }

class B extends A { B() { System.out.print("B "); } }

class C extends B { C() { System.out.print("C"); } }

Predict the output

Every constructor starts with an implicit super(), so C() first calls B(), which first calls A(). The parent is built before the child adds its part. C++ destructors run the other way, child first.

What does this print: static and instance initializer blocks with two objects?

Experiencedconstructorsstatic
Java
public class Main {
    public static void main(String[] args) {
        new Box();
        new Box();
    }
}

class Box {
    static { System.out.print("static "); }

    { System.out.print("init "); }

    Box() { System.out.print("ctor "); }
}

Predict the output

A static initializer runs once, when the class is initialized on first use; an instance initializer runs for every object, before the constructor body. So the first new Box() prints all three and the second only init ctor.

Can a constructor be private? When would you make one private?

Experiencedconstructors

Yes. A private constructor means only the class itself can create instances. Uses: a singleton, static factories (LocalDate.of(2026, 1, 15) decides what to return), utility classes such as java.lang.Math, and builders. A class whose constructors are all private cannot be subclassed outside the top-level class that contains it, since a subclass constructor must call super(...).

What are the this and super keywords used for?

Fresherconstructors

this refers to the current object; super refers to the parent class's part of it. this tells a field from a parameter (this.name = name;) and calls another constructor (this(size, "thin");). super calls the parent constructor or the parent version of an overridden method (super.info()). Neither exists in a static method.

What is the difference between static and instance members?

Fresherstatic

An instance member belongs to each object, which has its own copy of every instance field; a static member belongs to the class and is shared by all objects. Static members are reached through the class (Math.max(...)), have no this, and suit counters, constants and utility methods. More in Java static members.

Which methods does every Java class inherit from Object?

Fresheroops basicsequality

Every class inherits equals(), hashCode(), toString(), getClass(), clone(), wait(), notify(), notifyAll() and finalize() from java.lang.Object. Override equals() and hashCode() together or hash collections break; the default toString() prints something like Point@1b6d3586; finalize() is deprecated for removal since Java 18 (JEP 421).

Does Java have destructors? How is cleanup done?

Fresherconstructorsmemory

No. The garbage collector frees memory when an object becomes unreachable, at a time you do not control. For files, sockets and connections, implement AutoCloseable and use try-with-resources, which calls close() deterministically:

Java
public class Main {
    public static void main(String[] args) {
        try (Resource a = new Resource("db"); Resource b = new Resource("file")) {
            System.out.println("working");
        }
    }
}

class Resource implements AutoCloseable {
    private final String name;

    Resource(String name) {
        this.name = name;
        System.out.println("open " + name);
    }

    @Override
    public void close() { System.out.println("close " + name); }
}

It prints open db, open file, working, close file, close db: resources close in reverse order. See Java try-catch.

What is the difference between a shallow copy and a deep copy?

Experiencedconstructorsmemory

A shallow copy copies the fields as they are, so referenced objects are shared; a deep copy also copies the referenced objects, so the two are independent.

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Order original = new Order(new ArrayList<>(List.of("tea")));
        Order shallow = new Order(original, false);
        Order deep = new Order(original, true);
        original.items.add("cake");
        System.out.println(shallow.items + " " + deep.items);
    }
}

class Order {
    final List<String> items;

    Order(List<String> items) { this.items = items; }

    Order(Order other, boolean deep) {
        this.items = deep ? new ArrayList<>(other.items) : other.items;
    }
}

It prints [tea, cake] [tea]: the shallow copy saw the change. Object.clone() is shallow, so prefer a copy constructor.

How do you model a linked list node or a tree node as a class?

Fresherclasses

A node is a small class holding a value and references to other nodes: next for a linked list, left and right for a binary tree.

Java
public class Main {
    public static void main(String[] args) {
        Node head = new Node(1, new Node(2, new Node(3, null)));
        Node prev = null;
        while (head != null) {
            Node next = head.next;
            head.next = prev;
            prev = head;
            head = next;
        }
        for (Node n = prev; n != null; n = n.next) {
            System.out.print(n.val + " ");
        }
        System.out.println();
    }
}

class Node {
    int val;
    Node next;

    Node(int val, Node next) {
        this.val = val;
        this.next = next;
    }
}

It reverses 1 -> 2 -> 3 by re-pointing references and prints 3 2 1. Most linked list and tree problems start with this class.

Inheritance and polymorphism interview questions

Overloading versus overriding, what is polymorphic in Java, the diamond problem, and the output questions most candidates miss.

What is the difference between method overloading and method overriding?

Fresherpolymorphism

Overloading is several methods with the same name and different parameter lists, chosen at compile time from the argument types. Overriding is a subclass replacing an inherited method with the same signature, chosen at runtime from the object.

Java
public class Main {
    public static void main(String[] args) {
        Printer p = new LoudPrinter();
        p.print(7);
        p.print("hi");
    }
}

class Printer {
    void print(int x) { System.out.println("int " + x); }

    void print(String s) { System.out.println("String " + s); }
}

class LoudPrinter extends Printer {
    @Override
    void print(String s) { System.out.println("STRING " + s.toUpperCase()); }
}

It prints int 7 and STRING HI. See method overloading.

What is operator overloading? Does Java support it?

Fresherpolymorphismjava vs c++

Operator overloading lets a class define what an operator such as + or == does for its objects. C++ (operator+) and Python (__add__, __eq__) support it. Java does not, apart from the built-in + on strings, so you call methods such as BigDecimal.add.

Python
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __eq__(self, other):
        return isinstance(other, Vector) and (self.x, self.y) == (other.x, other.y)

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

print(Vector(1, 2) + Vector(3, 4), Vector(1, 2) == Vector(1, 2))

It prints Vector(4, 6) True. See C++ operator overloading.

What does this print: a field redeclared in a subclass, read through a parent reference?

Experiencedpolymorphisminheritance
Java
public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.println(a.name + " " + a.getName());
    }
}

class Animal {
    String name = "Animal";

    String getName() { return name; }
}

class Dog extends Animal {
    String name = "Dog";

    @Override
    String getName() { return name; }
}

Predict the output

Methods are polymorphic, fields are not. a.name is resolved at compile time from the reference type Animal, while a.getName() is overridden and dispatched at runtime to Dog. A subclass field with the same name hides the parent's, and both exist in the object. It compiles, and it is almost always a bug.

What does this print: a static and an instance method called through a parent reference?

Experiencedpolymorphismstatic
Java
public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.who() + " " + p.me());
    }
}

class Parent {
    static String who() { return "Parent"; }

    String me() { return "Parent"; }
}

class Child extends Parent {
    static String who() { return "Child"; }

    @Override
    String me() { return "Child"; }
}

Predict the output

Static methods are hidden, not overridden. A static call binds at compile time to the reference type, so p.who() calls Parent.who(), while the instance method me() is dispatched at runtime to Child. @Override on a static method is a compile error.

What does this print: null passed to overloads for Object and String?

Experiencedpolymorphism
Java
public class Main {
    static void show(Object o) { System.out.println("Object"); }

    static void show(String s) { System.out.println("String"); }

    public static void main(String[] args) {
        show(null);
    }
}

Predict the output

null matches both overloads, and Java picks the most specific: String is a subtype of Object, so show(String) wins.

Follow-up: what if you add show(Integer i)? The call stops compiling as ambiguous, because neither String nor Integer is more specific; choose with show((Integer) null).

Why does Java not support multiple inheritance of classes? What is the diamond problem?

Experiencedinheritanceinterfaces

If D inherits from B and C, and both override a method of A, which version does D get? Java avoids this for classes by allowing one superclass. Interface default methods can still clash, and then the class must override and choose:

Java
public class Main {
    public static void main(String[] args) {
        System.out.println(new SmartPhone().click());
    }
}

interface Camera {
    default String click() { return "photo"; }
}

interface Phone {
    default String click() { return "call"; }
}

class SmartPhone implements Camera, Phone {
    @Override
    public String click() { return Camera.super.click() + " + " + Phone.super.click(); }
}

It prints photo + call; without the override it does not compile. Python resolves the diamond with the MRO.

What does this print: an overridden method called from the parent constructor?

Seniorconstructorspolymorphism
Java
public class Main {
    public static void main(String[] args) {
        new Derived();
    }
}

class Base {
    Base() { describe(); }

    void describe() { System.out.println("base"); }
}

class Derived extends Base {
    String name = "derived";

    @Override
    void describe() { System.out.println("name=" + name); }
}

Predict the output

Base's constructor runs before Derived's field initializers, but the object is already a Derived, so describe() dispatches to the override and reads name while it is still null. C++ differs: during Base's constructor a virtual call runs Base::describe(). The rule from Effective Java: constructors must not call overridable methods.

What is upcasting and downcasting? What does instanceof do?

Fresherpolymorphism

Upcasting treats a subclass object as its parent type (Animal a = new Dog();) and is always safe. Downcasting goes back (Dog d = (Dog) a;) and throws ClassCastException if the object is not that type. instanceof checks first, and since Java 16 binds a variable:

Java
public class Main {
    public static void main(String[] args) {
        Object[] things = { "hello", 42, 3.5 };
        for (Object o : things) {
            if (o instanceof String s) {
                System.out.println("String of length " + s.length());
            } else if (o instanceof Integer i) {
                System.out.println("Integer doubled " + (i * 2));
            } else {
                System.out.println("Other: " + o.getClass().getSimpleName());
            }
        }
    }
}

It prints String of length 5, Integer doubled 84, Other: Double.

What does final mean on a variable, a method and a class?

Fresherinheritanceimmutability

final means it cannot change further. A final variable is assigned once, a final method cannot be overridden, and a final class cannot be extended (String, Integer). The trap: a final reference is not an immutable object. final List<String> list cannot point to another list, but list.add("x") still works.

What is a covariant return type?

Seniorpolymorphism

A covariant return type lets an overriding method return a subtype of the parent method's return type (Java 5 and later).

class Animal {
    Animal reproduce() { return new Animal(); }
}

class Dog extends Animal {
    @Override
    Dog reproduce() { return new Dog(); }   // Dog is a subtype of Animal
}

Callers holding a Dog get a Dog back without a cast. Parameters have no such freedom: changing a parameter type creates an overload, not an override.

Abstraction and interfaces interview questions

Abstract classes, interfaces, functional and marker interfaces, and the Python equivalents.

What is abstraction in OOPs? Give a real example.

Fresherabstraction

Abstraction is showing what an object does and hiding how, so callers depend on a simple contract. With List<String> names = new ArrayList<>(); you call add and get without caring how items are stored. Interfaces and abstract classes are the tools, and the hidden part can change without breaking callers.

What is the difference between an abstract class and an interface?

Fresherabstractioninterfaces

An abstract class is a partly built class that can hold state and constructors; an interface is a contract, and a class can implement many interfaces but extend one class.

Abstract classInterface
MethodsAbstract and concreteAbstract; default and static since Java 8, private since Java 9
FieldsAny instance fieldsOnly public static final constants
ConstructorsYesNo

See abstract classes.

When should you use an abstract class instead of an interface?

Seniorabstractioninterfaces

Use an interface for a capability that unrelated classes share, and an abstract class when related classes share state and code. A String and an Employee can both be Comparable; a family with common fields and a fixed algorithm fits an abstract class. Default to an interface, which stays open to any class and to lambdas. More in Java interfaces.

What does this print: an abstract class with its own constructor?

Fresherabstractionconstructors
Java
public class Main {
    public static void main(String[] args) {
        Shape s = new Square(2);
        System.out.print(s.area());
    }
}

abstract class Shape {
    Shape() { System.out.print("Shape "); }

    abstract double area();
}

class Square extends Shape {
    private final double side;

    Square(double side) {
        this.side = side;
        System.out.print("Square ");
    }

    @Override
    double area() { return side * side; }
}

Predict the output

An abstract class cannot be created with new Shape(), but its constructor runs whenever a subclass is created. new Square(2) calls super() first, which prints Shape , then Square , and area() returns the double 4.0. Use that constructor for the fields every subclass shares.

Can an interface have fields or a constructor?

Fresherinterfaces

No constructor and no instance fields. Variables declared in an interface are implicitly public static final constants:

interface Limits {
    int MAX_USERS = 100;          // public static final, even without the keywords
    void check(int users);
}

An interface has no state to initialize, and you never instantiate one directly. Implementing an interface only to use its constants is an anti-pattern; use a final class or an enum.

What is a functional interface?

Experiencedinterfaceslambdas

A functional interface has exactly one abstract method, so a lambda or method reference can implement it. default and static methods do not count, nor do abstract redeclarations of Object's public methods, so Comparator (it declares equals) is functional.

Java
public class Main {
    @FunctionalInterface
    interface Discount {
        double apply(double price);
    }

    public static void main(String[] args) {
        Discount none = p -> p;
        Discount festive = p -> p * 0.8;
        Discount flat = p -> Math.max(0, p - 50);
        System.out.println(none.apply(200) + " " + festive.apply(200) + " " + flat.apply(200));
    }
}

It prints 200.0 160.0 150.0. More in Java lambdas.

What is a marker interface?

Seniorinterfaces

A marker interface has no methods; implementing it tags the class for an instanceof check. Serializable is the standard example: ObjectOutputStream refuses objects that do not implement it. Annotations are the newer way to attach metadata, but a marker interface is a type, so the compiler can check void save(Serializable s).

How do you create an abstract class in Python?

Experiencedabstractionjava vs python

Inherit from abc.ABC and mark the required methods with @abstractmethod; Python then refuses to instantiate any class that still has an abstract method.

Python
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        ...

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2

print(Square(3).area())
try:
    Shape()
except TypeError as e:
    print(e)

It prints 9, then Can't instantiate abstract class Shape with abstract method area (Python 3.11's wording; 3.12 rephrased it). Unlike Java's compile-time check, this one happens when the object is created.

SOLID principles and design patterns interview questions

The five SOLID principles, composition over inheritance, and the common patterns: singleton, factory, strategy, observer, builder.

What are the SOLID principles?

Freshersolid

Five design rules for classes that are easy to change:

  1. Single responsibility: one reason to change.
  2. Open/closed: add behavior with new classes, not edits.
  3. Liskov substitution: a subclass works anywhere its parent does.
  4. Interface segregation: no class implements methods it does not need.
  5. Dependency inversion: depend on abstractions, not concrete classes.

Explain the single responsibility principle with an example.

Experiencedsolid

A class should have one reason to change. If tax rules, PDF layout and the database schema can each force edits to Invoice, it has three responsibilities:

// Before: three reasons to change
class Invoice {
    double total() { ... }          // business rules
    byte[] toPdf() { ... }          // presentation
    void saveToDb() { ... }         // persistence
}

// After: one each
class Invoice { double total() { ... } }
class InvoicePdfRenderer { byte[] render(Invoice inv) { ... } }
class InvoiceRepository { void save(Invoice inv) { ... } }

Now a layout change cannot break the tax calculation. Split when concerns change for different reasons, not into one-method classes.

What is the open/closed principle?

Experiencedsolid

Classes should be open for extension and closed for modification: you add behavior by writing a new class, not by editing one that works.

Java
public class Main {
    public static void main(String[] args) {
        Checkout checkout = new Checkout();
        System.out.println(checkout.complete(new Upi(), 499));
        System.out.println(checkout.complete(new Card(), 1299));
    }
}

interface PaymentMethod { String pay(int amount); }

class Upi implements PaymentMethod {
    public String pay(int amount) { return "UPI paid " + amount; }
}

class Card implements PaymentMethod {
    public String pay(int amount) { return "Card paid " + amount; }
}

class Checkout {
    String complete(PaymentMethod method, int amount) { return method.pay(amount); }
}

It prints UPI paid 499 and Card paid 1299. A wallet is one new class, and Checkout stays untouched. The violation is an if (type.equals("upi")) chain inside Checkout.

What does this print: a Square resized through a Rectangle method? Which SOLID principle does it break?

Seniorsolidinheritance
Java
public class Main {
    static void resize(Rectangle r) {
        r.setWidth(5);
        r.setHeight(4);
        System.out.println(r.area());
    }

    public static void main(String[] args) {
        resize(new Square());
    }
}

class Rectangle {
    protected int width, height;

    void setWidth(int w) { width = w; }

    void setHeight(int h) { height = h; }

    int area() { return width * height; }
}

class Square extends Rectangle {
    @Override
    void setWidth(int w) { width = w; height = w; }

    @Override
    void setHeight(int h) { width = h; height = h; }
}

Predict the output

Square keeps itself square by setting both sides in each setter, so setHeight(4) also makes the width 4: the area is 16, not the 20 resize expects. That breaks the Liskov substitution principle: a subtype must honor its parent's behavior, not only its signatures. Fix it with immutable shapes, or a shared Shape interface.

What is the interface segregation principle?

Experiencedsolidinterfaces

No class should be forced to implement methods it does not use, so split a fat interface into small, focused ones:

// Fat: a robot must pretend to eat
interface Worker { void work(); void eat(); }

// Segregated
interface Workable { void work(); }
interface Eater { void eat(); }

class Human implements Workable, Eater { ... }
class Robot implements Workable { ... }

The symptom of a violation is an implementation that throws UnsupportedOperationException or leaves a method empty.

What is the difference between dependency inversion and dependency injection?

Seniorsoliddesign patterns

Dependency inversion is the principle: high-level code depends on an abstraction, not a concrete class. Dependency injection is one way to get there: the object receives its dependencies, usually through the constructor, instead of creating them with new.

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        FakeNotifier fake = new FakeNotifier();
        new OrderService(fake).place("asha");
        System.out.println(fake.sent);
    }
}

interface Notifier { void send(String to, String message); }

class FakeNotifier implements Notifier {
    final List<String> sent = new ArrayList<>();

    public void send(String to, String message) { sent.add(to + ": " + message); }
}

class OrderService {
    private final Notifier notifier;

    OrderService(Notifier notifier) { this.notifier = notifier; }

    void place(String user) { notifier.send(user, "order placed"); }
}

It prints [asha: order placed]; a test passes a fake sender.

What is composition, and why prefer composition over inheritance?

Experienceddesigninheritance

Composition builds a class from objects it holds (has-a) instead of extending one (is-a). Prefer it because inheritance exposes every public method of the parent and ties you to its implementation. java.util.Stack extends Vector, so it accepts list operations that break the stack:

Java
import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);
        stack.push(2);
        stack.add(0, 99);
        System.out.println(stack + " pop=" + stack.pop());
    }
}

It prints [99, 1, 2] pop=2. A stack that holds an ArrayDeque cannot be misused that way.

What is the difference between association, aggregation and composition?

Experienceddesign

They differ in ownership and lifetime. Association: objects use each other and live independently (a teacher and a student). Aggregation: has-a, and the parts can outlive the whole (a team and its players). Composition: the whole owns its parts, and they die with it (a house and its rooms).

What are coupling and cohesion?

Experienceddesign

Cohesion is how closely the parts of one class belong together; coupling is how much a class depends on another's internals. Aim for high cohesion and low coupling. OrderService creating new MySqlOrderDao() is tightly coupled; taking an OrderRepository interface in its constructor is loose.

How do you implement a thread-safe singleton in Java?

Seniordesign patterns

A singleton guarantees one instance with a global access point. The thread-safe versions are the enum singleton and the holder idiom; both rely on the JVM running class initialization once, under a lock.

Java
public class Main {
    public static void main(String[] args) {
        System.out.println("start");
        Config a = Config.get();
        Config b = Config.get();
        System.out.println(a == b);
    }
}

class Config {
    private Config() { System.out.println("loading config"); }

    private static class Holder {
        static final Config INSTANCE = new Config();
    }

    static Config get() { return Holder.INSTANCE; }
}

It prints start, loading config, true: created lazily, once. Double-checked locking works only with a volatile field.

What is the factory design pattern?

Experienceddesign patterns

A factory is a method or class that creates objects, so callers ask for a notification for a channel and never name the concrete class. It keeps new ConcreteClass() in one place.

Java
import java.util.List;

public class Main {
    public static void main(String[] args) {
        for (String channel : List.of("email", "sms")) {
            System.out.println(NotificationFactory.create(channel).send("OTP 4821"));
        }
    }
}

interface Notification { String send(String message); }

class EmailNotification implements Notification {
    public String send(String message) { return "email: " + message; }
}

class SmsNotification implements Notification {
    public String send(String message) { return "sms: " + message; }
}

class NotificationFactory {
    static Notification create(String channel) {
        return switch (channel) {
            case "email" -> new EmailNotification();
            case "sms" -> new SmsNotification();
            default -> throw new IllegalArgumentException("unknown channel " + channel);
        };
    }
}

It prints email: OTP 4821 and sms: OTP 4821. List.of and Integer.valueOf are static factories in the JDK.

What is the strategy pattern?

Seniordesign patterns

The strategy pattern puts interchangeable algorithms behind one interface, so the caller picks one at runtime instead of branching with if/else. With lambdas a strategy is one line:

Java
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.DoubleUnaryOperator;

public class Main {
    public static void main(String[] args) {
        Map<String, DoubleUnaryOperator> shipping = new LinkedHashMap<>();
        shipping.put("standard", kg -> 40 + 10 * kg);
        shipping.put("express", kg -> 100 + 25 * kg);
        shipping.put("pickup", kg -> 0);
        for (var entry : shipping.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue().applyAsDouble(2));
        }
    }
}

It prints standard 60.0, express 150.0 and pickup 0.0. Comparator is the strategy everyone uses: list.sort(byName) versus list.sort(byAge).

What is the observer pattern?

Seniordesign patterns

A subject keeps a list of listeners and notifies all of them when something happens, without knowing what they do.

Java
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        OrderEvents events = new OrderEvents();
        events.subscribe(id -> System.out.println("email sent for " + id));
        events.subscribe(id -> System.out.println("stock updated for " + id));
        events.publish("A17");
    }
}

class OrderEvents {
    private final List<Consumer<String>> listeners = new ArrayList<>();

    void subscribe(Consumer<String> listener) { listeners.add(listener); }

    void publish(String orderId) {
        for (Consumer<String> listener : listeners) listener.accept(orderId);
    }
}

It prints email sent for A17 then stock updated for A17. Pitfall: listeners that are never removed leak memory.

What is the builder pattern, and when do you use it?

Seniordesign patterns

A builder sets an object's values through named calls and creates it in one build(). Use it when a class has many optional parameters, instead of telescoping constructors such as new User("Asha", null, null, 0, true).

Java
public class Main {
    public static void main(String[] args) {
        UserProfile p = new UserProfile.Builder("Asha")
                .email("asha@example.com")
                .city("Pune")
                .build();
        System.out.println(p);
    }
}

class UserProfile {
    private final String name;
    private final String email;
    private final String city;

    private UserProfile(Builder b) {
        name = b.name;
        email = b.email;
        city = b.city;
    }

    @Override
    public String toString() { return name + " <" + email + "> " + city; }

    static class Builder {
        private final String name;
        private String email = "none";
        private String city = "unknown";

        Builder(String name) { this.name = name; }

        Builder email(String email) { this.email = email; return this; }

        Builder city(String city) { this.city = city; return this; }

        UserProfile build() { return new UserProfile(this); }
    }
}

It prints Asha <asha@example.com> Pune. build() is the one place to validate, and the result can be immutable.

OOPs in Java vs Python vs C++

How the same ideas look in Java, Python and C++, with the Python traps as runnable code.

How is OOP different in Java, Python and C++?

Experiencedjava vs pythonjava vs c++

They differ mostly in how strict they are.

JavaPythonC++
Multiple inheritance of classesNo; many interfacesYes, by the MROYes
Access controlEnforcedConventions onlyEnforced, plus friend
Methods dynamic by defaultYesYesNo, only virtual ones
Method overloadingYesNo (last def wins)Yes
CleanupGarbage collectorReference counting plus a cycle collector (CPython)Destructors (RAII)

What is self in Python?

Fresherjava vs python

self is the first parameter of an instance method: the object the method was called on. Python passes it for you, so c.increment() is shorthand for Counter.increment(c).

Python
class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1

c = Counter()
c.increment()
Counter.increment(c)
print(c.value)

It prints 2. self is a convention, not a keyword. See Python classes.

What does this print: single and double underscore attributes in Python?

Experiencedjava vs pythonaccess control
Python
class Account:
    def __init__(self):
        self.__pin = 1234
        self._owner = "asha"

a = Account()
print(hasattr(a, "__pin"), a._owner, a._Account__pin)

Predict the output

A name with two leading underscores inside a class is mangled to _ClassName__name, so self.__pin is stored as _Account__pin. hasattr(a, "__pin") is False, while a._Account__pin still works. A single underscore is only a convention, and nothing stops access. Mangling avoids name clashes in subclasses; it is not security.

What does this print: a list defined as a Python class attribute?

Experiencedjava vs pythonstatic
Python
class Dog:
    tricks = []

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)

rex = Dog("Rex")
fido = Dog("Fido")
rex.add_trick("sit")
print(fido.tricks)

Predict the output

tricks = [] is a class attribute: one list on the class, shared by every instance. self.tricks.append(...) finds that list and mutates it, so Fido sees Rex's trick. Create per-object state in __init__ (self.tricks = []); class attributes are safe for constants and values you only read.

What does this print: a Python diamond declared as D(B, C)?

Seniorjava vs pythoninheritance
Python
class A:
    def hi(self):
        return "A"

class B(A):
    def hi(self):
        return "B"

class C(A):
    def hi(self):
        return "C"

class D(B, C):
    pass

print(D().hi(), [k.__name__ for k in D.__mro__])

Predict the output

Python resolves methods along the MRO (method resolution order), computed by C3 linearization: the class, then its parents left to right, with a shared base after every class that inherits from it. For D(B, C) that is D, B, C, A, object, so hi() comes from B. More in Python inheritance.

Does Python support method overloading?

Experiencedjava vs pythonpolymorphism

Not the way Java does. A second def with the same name replaces the first, so only the last exists. Use default arguments, *args, or functools.singledispatch to choose by the type of the first argument (singledispatchmethod, Python 3.8+, does it for methods).

Python
from functools import singledispatch

class Calc:
    def add(self, a, b):
        return a + b

    def add(self, a, b, c=0):  # replaces the method above
        return a + b + c

print(Calc().add(1, 2), Calc().add(1, 2, 3))

@singledispatch
def describe(x):
    return "something"

@describe.register
def _(x: int):
    return f"int {x}"

@describe.register
def _(x: str):
    return f"str {x!r}"

print(describe(5), describe("hi"), describe(2.5))

It prints 3 6, then int 5 str 'hi' something.

What is @property in Python, and why use it instead of getters and setters?

Experiencedjava vs pythonencapsulation

@property (a decorator) makes a method read like an attribute, and @name.setter validates assignments. You can add a property later without changing any caller, so Python code does not write get_x() and set_x() up front.

Python
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius  # goes through the setter

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("below absolute zero")
        self._celsius = value

    @property
    def fahrenheit(self):
        return self._celsius * 9 / 5 + 32

t = Temperature(25)
print(t.fahrenheit)
try:
    t.celsius = -300
except ValueError as e:
    print("Refused:", e)

It prints 77.0, then Refused: below absolute zero. fahrenheit has no setter, so it is read-only.

What is duck typing, and how is it different from using interfaces?

Experiencedjava vs pythonpolymorphism

Duck typing accepts an object for what it can do, not the type it declares: if it has speak(), you can call speak(). Python checks at runtime; Java requires implements Speaker and checks at compile time.

Python
class Duck:
    def speak(self):
        return "Quack"

class Robot:
    def speak(self):
        return "Beep"

def chorus(things):
    return " ".join(t.speak() for t in things)

print(chorus([Duck(), Robot()]))

It prints Quack Beep although Duck and Robot share no parent. A missing method fails only when that line runs.

What is a virtual function in C++? What happens without virtual?

Experiencedjava vs c++polymorphism

A virtual function is dispatched at runtime from the object's actual type when called through a base pointer or reference. Without virtual, C++ binds the call at compile time from the pointer's type, the opposite of Java's default.

#include <iostream>

struct Animal {
    void name() { std::cout << "Animal\n"; }           // not virtual
    virtual void sound() { std::cout << "...\n"; }
    virtual ~Animal() = default;
};

struct Dog : Animal {
    void name() { std::cout << "Dog\n"; }
    void sound() override { std::cout << "Woof\n"; }
};

int main() {
    Dog d;
    Animal& a = d;
    a.name();   // prints Animal: bound at compile time
    a.sound();  // prints Woof: dispatched at runtime
}

A pure virtual function (= 0) makes the class abstract. See C++ virtual functions.

Why does a C++ base class need a virtual destructor?

Seniorjava vs c++memory

Deleting a derived object through a base class pointer whose destructor is not virtual is undefined behavior; usually the derived destructor does not run, so its resources leak.

struct Base {
    ~Base() {}                 // should be: virtual ~Base() = default;
};

struct Buffer : Base {
    int* data = new int[1000];
    ~Buffer() { delete[] data; }
};

int main() {
    Base* p = new Buffer();
    delete p;                  // undefined behavior: ~Buffer() is not called
}

A base class destructor should be either public and virtual, or protected and non-virtual. See the cppreference delete expression page.

What is the difference between a struct and a class in C++?

Fresherjava vs c++

The only difference is default access: members and base classes of a struct are public by default, those of a class are private. Both can have constructors, methods, inheritance and virtual functions.

struct Point { int x, y; };        // x and y are public
class Account { int balance; };    // balance is private

By convention struct holds plain data and class holds types with invariants.

What is a friend function in C++? Does it break encapsulation?

Experiencedjava vs c++access control

A friend function (or class) is outside code that a class explicitly allows to read its private and protected members. It is not a member and has no this.

#include <iostream>

class Money {
    long paise;
public:
    explicit Money(long p) : paise(p) {}
    friend std::ostream& operator<<(std::ostream& out, const Money& m);
};

std::ostream& operator<<(std::ostream& out, const Money& m) {
    return out << m.paise / 100 << "." << m.paise % 100 / 10 << m.paise % 10;
}

The classic use is operator<<, whose left operand is the stream, so it cannot be a member. Friendship is granted by the class and is neither transitive nor inherited.

OOPs interview questions for experienced developers

Equality contracts, immutability, records and sealed types, dispatch internals and object-oriented design rounds.

What does this print: equal Point objects added to a HashSet? What is the equals and hashCode contract?

Seniorequalitycollections
Java
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<Point> set = new HashSet<>();
        set.add(new Point(1, 2));
        set.add(new Point(1, 2));
        System.out.println(set.size() + " " + new Point(1, 2).equals(new Point(1, 2)));
    }
}

class Point {
    final int x, y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof Point p && p.x == x && p.y == y;
    }
}

Predict the output

It prints 2 true. hashCode() was not overridden, so the equal points keep their identity hashes; with different hash codes HashSet never calls equals() and stores both. (A rare identity hash collision would give 1.) The contract: equal objects must have equal hash codes, and both methods must stay stable while the object is a key. Override them together, or use a record. See Java HashMap.

What is a record in Java, and when should you use one?

Experiencedimmutabilityequality

A record (final since Java 16) is a class for immutable data: you declare the components, and the compiler generates the private final fields, constructor, accessors (x(), not getX()), equals(), hashCode() and toString().

Java
public class Main {
    record Point(int x, int y) {
        Point {
            if (x < 0 || y < 0) throw new IllegalArgumentException("negative");
        }

        double distance() { return Math.sqrt(x * x + y * y); }
    }

    public static void main(String[] args) {
        Point a = new Point(3, 4);
        System.out.println(a + " " + a.equals(new Point(3, 4)) + " " + a.distance());
    }
}

It prints Point[x=3, y=4] true 5.0. Records are only shallowly immutable: a List component can still change unless you copy it.

How do you create an immutable class in Java?

Seniorimmutability

Make the class final (or its constructors private), every field private final, provide no setters (modifying methods return a new object), and copy mutable inputs and outputs so outside code never holds your internal state.

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> source = new ArrayList<>(List.of("a"));
        Playlist p = new Playlist(source);
        source.add("b");
        Playlist q = p.with("c");
        System.out.println(p.songs() + " " + q.songs());
        try {
            p.songs().add("x");
        } catch (UnsupportedOperationException e) {
            System.out.println("read-only");
        }
    }
}

final class Playlist {
    private final List<String> songs;

    Playlist(List<String> songs) { this.songs = List.copyOf(songs); }

    List<String> songs() { return songs; }

    Playlist with(String song) {
        List<String> next = new ArrayList<>(songs);
        next.add(song);
        return new Playlist(next);
    }
}

It prints [a] [a, c], then read-only. Immutable objects are thread-safe without locks and safe as map keys.

What is the difference between Comparable and Comparator?

Experiencedcollectionsinterfaces

Comparable gives a class one natural order through compareTo(), written inside the class. A Comparator is a separate object for any other order.

Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {
    record Student(String name, int marks) implements Comparable<Student> {
        public int compareTo(Student other) { return Integer.compare(marks, other.marks); }
    }

    public static void main(String[] args) {
        List<Student> list = new ArrayList<>(List.of(
                new Student("Ravi", 72), new Student("Asha", 91), new Student("Meera", 72)));
        Collections.sort(list);
        System.out.println(list.stream().map(Student::name).toList());
        list.sort(Comparator.comparingInt(Student::marks).reversed().thenComparing(Student::name));
        System.out.println(list.stream().map(Student::name).toList());
    }
}

It prints [Ravi, Meera, Asha] (by marks; the stable sort keeps the two 72s in order), then [Asha, Meera, Ravi]. Use Integer.compare rather than a - b, which overflows.

What does this print: a HashSet subclass that counts the elements added?

Seniorinheritancedesign
Java
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        CountingSet<String> set = new CountingSet<>();
        set.addAll(List.of("a", "b", "c"));
        System.out.println(set.added);
    }
}

class CountingSet<E> extends HashSet<E> {
    int added = 0;

    @Override
    public boolean add(E e) {
        added++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        added += c.size();
        return super.addAll(c);
    }
}

Predict the output

addAll adds 3 to the counter, then super.addAll (inherited from AbstractCollection) calls add() for each element. That add() is our override, so every element is counted twice, giving 6. The subclass depends on an undocumented detail of its parent: the fragile base class problem, fixed by holding a Set instead of extending one.

What are sealed classes, and how do they change polymorphism in Java?

Seniorpolymorphisminterfaces

A sealed class or interface lists exactly which classes may extend it (final since Java 17). With pattern matching in switch (final since Java 21, JEP 441), the compiler checks that a switch covers every subtype.

Java
public class Main {
    sealed interface Shape permits Circle, Rect {}

    record Circle(double r) implements Shape {}

    record Rect(double w, double h) implements Shape {}

    static double area(Shape s) {
        return switch (s) {
            case Circle c -> Math.PI * c.r() * c.r();
            case Rect r -> r.w() * r.h();
        };
    }

    public static void main(String[] args) {
        System.out.printf("%.2f %.2f%n", area(new Circle(1)), area(new Rect(2, 3)));
    }
}

It prints 3.14 6.00. There is no default; add a Triangle to permits and the switch stops compiling until it handles it.

How does runtime polymorphism work internally?

Seniorpolymorphismjvm

Through a method table, a vtable, in HotSpot and the major C++ compilers (neither standard requires it). Each class has a table of pointers to its overridable methods, an override takes the same slot as the method it replaces, and each object points to its class. A virtual call reads that slot and jumps.

  • Interface calls in the JVM use a separate itable.
  • HotSpot's JIT inlines a call site that has seen one class, with a guard that deoptimizes if another appears.
  • Python looks methods up by name along the MRO at runtime.

What is the Law of Demeter?

Seniordesign

A method should call methods only on itself, its fields, its parameters and objects it creates, not on objects those calls return.

// Violates: the caller knows Order, Customer, Address and their structure
String city = order.getCustomer().getAddress().getCity();

// Better: ask the object that owns the knowledge
String city = order.shippingCity();

The chain couples the caller to three classes. Fluent APIs such as stream().filter().map() are not violations, because each call returns the same kind of object.

How would you approach an object-oriented design question such as "design a parking lot"?

Seniordesign

Clarify requirements, turn the nouns into classes and the verbs into methods, then walk one use case through the objects. The process and the trade-offs are graded, not the diagram.

  1. Ask: floors, vehicle and spot sizes, payment.
  2. Classes: ParkingLot, Floor, Spot, Vehicle, Ticket, PricingPolicy.
  3. Behavior: park(vehicle) returns a Ticket; unpark(ticket) frees the spot and returns the fee.
interface SpotFinder { Optional<Spot> find(List<Floor> floors, Vehicle v); }
interface PricingPolicy { long feeInPaise(Ticket t, Instant exit); }

class ParkingLot {
    private final List<Floor> floors;
    private final SpotFinder finder;
    private final PricingPolicy pricing;

    Ticket park(Vehicle v) { ... }
    long unpark(Ticket t) { ... }
}

Mention concurrency (two cars must not claim one spot) and keep money in integer units, not double.

In Python, what happens to __hash__ when you define __eq__?

Seniorequalityjava vs python

Defining __eq__ without __hash__ sets __hash__ to None, so instances become unhashable and cannot be set members or dict keys. That protects the rule that equal objects have equal hashes (see the __hash__ docs).

Python
from dataclasses import dataclass

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __eq__(self, other):
        return isinstance(other, Point) and (self.x, self.y) == (other.x, other.y)

print(Point(1, 2) == Point(1, 2))
try:
    {Point(1, 2)}
except TypeError as e:
    print(e)

@dataclass(frozen=True)
class FrozenPoint:
    x: int
    y: int

print(len({FrozenPoint(1, 2), FrozenPoint(1, 2)}))

It prints True, then unhashable type: 'Point', then 1. Define __hash__ from the same fields (return hash((self.x, self.y))) or use @dataclass(frozen=True), which generates both.

Preparing for the interview

How should I prepare for an OOPs interview?
Learn each pillar as one sentence plus one example, then the comparisons: abstract class versus interface, overloading versus overriding, composition versus inheritance. Write small programs with constructors, overriding and interfaces and predict their output before running them. For experienced roles add SOLID, five or six design patterns and one design problem such as a parking lot.
What OOPs questions are asked to freshers compared with experienced candidates?
Freshers get definitions and small code: the four pillars, class versus object, constructors, static, access modifiers, overloading versus overriding, and output questions. Experienced candidates are asked why: when to choose composition, how equals and hashCode interact, how to make a class immutable, which pattern fits a problem, and how to design a small system.
Which language should I use to answer OOPs questions?
Use the language on the job description, or your most fluent one. Java is the safest default because its OOP features are explicit: interfaces, access modifiers, abstract, @Override. In Python, be ready to explain the differences: no enforced private members, multiple inheritance with the MRO, and duck typing instead of declared interfaces.
Do freshers need to know design patterns and SOLID?
Usually the five SOLID principles by name with one example each, plus singleton and factory. Deeper pattern questions and design rounds start at about two years of experience. Explaining one pattern well beats listing twenty.
Are OOPs questions enough, or do I also need DSA?
For most software roles OOP is one round, and data structures and algorithms carry more weight. They overlap: linked list and tree problems start with a node class, heap and sorting problems need comparators, and designs like a min stack or a trie are graded on clean classes. Practise the problems linked on this page in your interview language.
Coddy programming languages illustration

Learn to code with Coddy

GET STARTED