본문으로 바로가기

Top Object-Oriented Programming Interview Questions and Answers

Prepare for OOP interviews with questions covering classes, objects, inheritance, polymorphism, encapsulation, abstraction, design principles, and practical coding scenarios.
2026년 9월 8일  · 15분 읽다

AI로 탐색하기

ChatGPTClaudePerplexity

Interviewers often use OOP questions to assess how well candidates understand the principles behind designing and organizing software.

These interviews typically go beyond memorizing definitions. You may be asked to explain class design, relationships between objects, long-term maintainability, and the architectural trade-offs of your decisions.

Whether you're preparing for an entry-level interview or a more experienced software engineering role, the following OOP interview questions can help you practice explaining concepts and applying them to real programming problems. There's something to learn and practice, whether you use Python, Java, or C++.

If, after reading, you want to study more about the foundational concepts of OOP, I recommend taking our Object-Oriented Programming in Python and Introduction to Object-Oriented Programming in Java courses to learn how to create classes, objects, and inheritance.

Basic OOP Interview Questions

In the initial interview phase, the interviewer can ask foundational questions to assess your knowledge of basic OOP concepts. Below are the common questions you are likely to encounter with sample answers to each. So starting at the beginning:

1. What is object-oriented programming?

Object-oriented programming (OOP) is a programming paradigm that organizes software around objects, which combine data and behavior. It models real-world entities to make code modular, reusable, and easier to maintain through core principles like encapsulation, inheritance, and polymorphism.

2. What is an object?

An object is an instance of a class. It contains state, represented by attributes or properties, and behavior, represented by methods. For example, if Car is a class, a specific car, such as a red Toyota with a particular speed, can be an object of that class.

3. What is a class?

A class is a blueprint or template for creating objects. It defines the attributes and methods that objects of that class can have. In the code snippet below, the Employee class defines what an employee object contains and what it can do.

# Define a class to represent an employee
class Employee:
    # Initialize a new instance with a given name
    def __init__(self, name):
        self.name = name  # Stores the employee's name

    # Simulates the employee performing work
    def work(self):
        print(f"{self.name} is working")

4. What is the difference between a class and an object?

A class defines the structure and behavior, while an object is a specific instance created from that class. For example, Employee can be a class, while Employee(“Alice”) and Employee(“Bob”) are two different objects created from it.

5. What are the four pillars of OOP?

The four commonly recognized pillars of OOP are:

  • Encapsulation: Bundling data and the methods that operate on it while controlling access.
  • Abstraction: Exposing essential behavior while hiding unnecessary implementation details.
  • Inheritance: Allowing one class to derive properties and behavior from another class.
  • Polymorphism: Allowing the same interface or method call to produce different behavior depending on the object.

6. What is a constructor?

A constructor is a special method that runs when an object is created. It is commonly used to initialize the object's attributes.

For example, Python uses __init__() as the initializer. So when Employee(“Alice”) runs, __init__() initializes the object's name attribute.

# Defines a class to represent an employee
class Employee:
    # Initialize a new instance with a given name
    def __init__(self, name):
        self.name = name

employee = Employee("Alice")

7. What is a method?

A method is a function defined inside a class that describes behavior associated with objects of that class. In the example below, bark() is a method of the Dog class.

# Define a class to represent a dog
class Dog:
    def bark(self):
        print("Woof!")

dog = Dog()
dog.bark()

8. What is an instance variable?

An instance variable is an attribute whose value belongs to a particular object. Different objects of the same class can have different values for their instance variables.

In our example below, both objects have a name variable, but alice.name and bob.name contain different values.

# Define a class to represent an employee
class Employee:
    def __init__(self, name):
        self.name = name

alice = Employee("Alice")
bob = Employee("Bob")

9. What are access modifiers?

Access modifiers control how class members can be accessed from different parts of a program. The common access levels include:

  • Public: Accessible from anywhere.
  • Private: Accessible only within the declaring class.
  • Protected: Accessible within the class and its subclasses.

OOP Principles Interview Questions

Having covered the basic questions, now let’s move into the common OOP principles interview questions. Interviewers usually ask these questions to test whether you can apply them rather than merely name them.

10. What is encapsulation in OOP?

Encapsulation is the practice of bundling data and the methods that operate on that data inside a class while controlling how the internal state can be accessed or modified.

For example, instead of allowing code to directly change a bank account balance, a class can provide methods such as deposit() and withdraw() that enforce rules. The balance is kept internally, and changes go through controlled behavior.

# Defines a BankAccount class with a private balance 
# It can be increased through deposits.
class BankAccount: 
    def __init__(self, balance): 
        self.__balance = balance 
 
    def deposit(self, amount): 
        if amount > 0: 
            self.__balance += amount

11. How does encapsulation improve maintainability?

Encapsulation reduces dependencies between different parts of a program. Other code interacts with an object's public interface instead of depending on its internal implementation.

12. What is the difference between encapsulation and data hiding?

Encapsulation focuses on combining data and behavior within a class and controlling access to them. Data hiding focuses specifically on restricting direct access to internal implementation details.

For example, making a bank account's balance private is data hiding, while keeping the balance and methods such as deposit() and withdraw() together within the BankAccount class demonstrates encapsulation.

13. What is abstraction in OOP?

Abstraction means exposing the essential features of an object while hiding unnecessary implementation details.

For example, when you call:

# Calls the start() method of the car object.
car.start()

You do not need to know every internal operation involved in starting the engine. The start() method provides a simpler interface to that functionality.

14. What is the difference between abstraction and encapsulation?

Abstraction focuses on what an object does, while encapsulation focuses on how data and behavior are organized and controlled.

For example, an abstract Payment interface might define a pay() operation without specifying how every payment method works. Encapsulation can then keep the implementation details and internal data inside each payment class.

15. What is an interface?

An interface defines a contract that implementing classes must follow. It specifies operations that a class should provide without necessarily defining their implementation.

For example, an interface called PaymentMethod could require a pay() method. Credit card, mobile payment, and bank transfer classes could each implement that method differently.

16. What is an abstract class?

An abstract class is a class intended to serve as a base for other classes. It can define shared behavior while leaving some methods for child classes to implement.

In the example below, a subclass such as Circle can provide the implementation of the area() method.

# Defines an abstract Shape class 
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):   #  requires subclasses to implement the area() method.
        pass

17. What is inheritance?

Inheritance allows a child class to acquire attributes and methods from a parent class. It can be useful when classes share a genuine is-a relationship.

Below is an example where Dog inherits the eat() method from Animal and adds its own bark() behavior.

# Demonstrates inheritance, where Dog inherits the eat() method from Animal.
class Animal:
    def eat(self):
        print("Eating")

class Dog(Animal):
    def bark(self):
        print("Barking")

18. What is the difference between a parent class and a child class?

A parent class, also called a base or superclass, provides common attributes and behavior. A child class, also called a subclass, inherits from the parent and can add or modify behavior.

In our example above, Animal is the parent class and Dog is the child class.

19. Can a child class override a parent method?

Yes. Method overriding occurs when a child class provides its own implementation of a method inherited from the parent class.

For example, calling Dog().speak() uses the Dog implementation rather than the inherited implementation.

# Demonstrates method overriding
# Dog provides its own version of the speak() method.
class Animal:
    def speak(self):
        print("Some sound")

class Dog(Animal):
    def speak(self):
        print("Woof")

20. What are the benefits and drawbacks of inheritance?

Inheritance can reduce duplicated code and provide a clear way to share common behavior. It can also support polymorphism by allowing different child classes to be treated as instances of a common parent type.

However, deep or poorly designed inheritance hierarchies can make code difficult to understand and change. A change in a parent class can also affect many child classes.

21. What is polymorphism?

Polymorphism means that the same interface or operation can work with different types of objects, with each object providing its own behavior.

In the example below, the same speak() call produces different results depending on the object.

# Demonstrates polymorphism 
# Calls the same speak() method on different animal objects.
class Dog:
    def speak(self):
        print("Woof")

class Cat:
    def speak(self):
        print("Meow")

for animal in [Dog(), Cat()]:
    animal.speak()

22. How does method overriding demonstrate polymorphism?

A parent class can define a common method, while child classes override that method with their own implementations.

For example, the code that works with a Payment reference can call process() without needing to know which specific payment implementation it received.

# Demonstrates polymorphism 
# Allows different payment types to implement process() differently.
class Payment:
    def process(self):
        pass

class CardPayment(Payment):
    def process(self):
        print("Processing card payment")

class CashPayment(Payment):
    def process(self):
        print("Processing cash payment")

23. What is runtime polymorphism?

Runtime polymorphism occurs when the implementation that should execute is determined at runtime based on the actual object involved.

24. How does polymorphism differ between Python, Java, and C++?

Although the underlying idea is similar, polymorphism differs in different languages, as follows:

  • Python commonly uses duck typing, inheritance, method overriding, and protocols.
  • Java commonly uses inheritance, interfaces, and method overriding for runtime polymorphism.
  • C++ supports polymorphism through inheritance, virtual functions, abstract classes, and templates.

Classes and Object Design Interview Questions

In this section, I will cover questions that interviewees usually use to test how you can actually design software with objects.

25. What is the difference between an instance member and a class or static member?

An instance member belongs to a specific object, so each object can have its own value. A class or static member belongs to the class itself and is shared across instances.

In the Python code below, name belongs to each individual employee, while company is shared by the class.

class Employee:
    company = "Acme"  # Class variable

    def __init__(self, name):
        self.name = name  # Instance variable

alice = Employee("Alice")
bob = Employee("Bob")

alice.name = "Alicia"

26. What is the purpose of a constructor?

A constructor or initializer prepares a newly created object for use. It assigns initial values, validates required inputs, or establishes the object's initial state.

For example, the code below defines the User template that assigns a unique username to each new user account created.

# Define class User
class User:
    def __init__(self, username):
        self.username = username  # Set the initial state

27. What happens during an object's lifecycle?

An object's lifecycle generally involves creation, initialization, use, and eventual destruction or garbage collection.

The exact process depends on the programming language:

  • Python and Java: An object is created and initialized before it is used, and its memory is eventually reclaimed when it is no longer referenced. 
  • C++: Gives developers more direct control over object lifetime, particularly for objects with automatic, dynamic, or static storage duration.

28. What is an immutable object?

An immutable object cannot have its state changed after it has been created. If a different value is needed, a new object is created instead.

For example, the object name in the Python code below is immutable, and changing its value creates a new object.

# Creates an object name
name = "Alice"
name = name + " Smith"  # Creates a new string

29. Why might you choose to make an object immutable?

Immutable objects are useful when the object's state should remain consistent throughout its lifetime. They reduce unintended side effects and can simplify concurrent or multi-threaded programs.

Let’s say you have a Money object representing an amount, and the currency could be immutable. Instead of modifying the existing amount, an operation such as addition could return a new Money object.

30. What responsibilities should a method have?

A method should generally have a clear, focused responsibility that relates to the behavior of its class. A method that tries to validate input, access a database, generate reports, send emails, and update unrelated objects is usually doing too much.

For example, the Python code below defines a BankAccount class with a deposit() method. The method checks whether the deposit amount is positive and raises a ValueError if the amount is zero or negative.

class BankAccount:
    def deposit(self, amount):
        # Checks that the deposit amount is positive.
        if amount <= 0:
            # Raises an error for invalid deposit amounts.
            raise ValueError("Amount must be positive")

31. What are common relationships between classes?

Common class relationships include:

  • Inheritance: one class derives from another.
  • Association: one object interacts with another.
  • Aggregation: an object contains or uses other objects that can exist independently.
  • Composition: an object owns other objects whose lifecycle is closely tied to its own.
  • Dependency: one class temporarily relies on another to perform an operation.

32. How would you design a class from a set of requirements?

I would first identify the class's responsibility, state, behavior, and relationships. Then I would determine which data should be exposed, which should remain internal, and what invariants the class must maintain.

For example, if I were to design a BankAccount class, I would specify that it stores an account number and balance, allows deposits and withdrawals, and prevents withdrawals greater than the available balance.

class BankAccount:
    # Initializes a bank account with an account number and starting balance.
    def __init__(self, account_number, balance=0):
        self.account_number = account_number
        self._balance = balance  # Stores the account balance internally.

    # Adds money to the account after validating the amount.
    def deposit(self, amount):
        # Ensures the deposit amount is positive.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self._balance += amount  # Updates the account balance.

    # Removes money from the account if sufficient funds are available.
    def withdraw(self, amount):
        # Ensures the withdrawal amount is positive.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        # Prevents withdrawals greater than the available balance.
        if amount > self._balance:
            raise ValueError("Insufficient funds")
        self._balance -= amount  # Deducts the amount from the balance.

    # Returns the current account balance.
    def get_balance(self):
        return self._balance

Inheritance, Composition, and Interfaces Interview Questions

I will now cover the common questions you are likely to encounter that test whether you properly understand inheritance, composition, and interfaces in OOP.

33. When should you use inheritance instead of composition?

I would use inheritance when there is a genuine and stable is-a relationship, and the child should conform to the behavior expected from the parent.

I would prefer a composition where one object has or uses another object, and the relationship is likely to change.

For example, the code below demonstrates a has-a relationship through composition where Car contains an Engine and uses it to start.

class Engine:
    # Defines the behavior of an engine.
    def start(self):
        print("Engine started")

class Car:
    # A Car has an Engine, demonstrating composition.
    def __init__(self):
        self.engine = Engine()

    # Starts the car by delegating to its Engine.
    def start(self):
        self.engine.start()

34. What is the difference between an “is-a” and a “has-a” relationship?

An ‘is-a’ relationship usually represents inheritance, while a has-a relationship usually represents composition or aggregation.

For example:

  • A Dog is an Animal.
  • A Car has an Engine.
  • A Library has Books.

35. Why is composition often preferred over inheritance?

Composition allows behavior to be assembled from smaller objects without creating a rigid class hierarchy. It can reduce coupling and make individual components easier to replace or test.

For example, instead of creating separate subclasses for every combination of notification type and delivery mechanism, a notification service could receive a MessageSender object and delegate sending to it. This makes it easier to replace the sender without changing the notification service itself.

36. What problems can inheritance create?

Inheritance can create tight coupling between parent and child classes. A change to the parent can unexpectedly affect multiple subclasses.

It can also produce deep or complicated hierarchies where it becomes difficult to determine where behavior originates. Another problem occurs when a child technically inherits behavior but does not genuinely satisfy the parent's expected contract.

37. What is multiple inheritance?

Multiple inheritance allows a class to inherit from more than one parent class. In our Python example below, MultiFunctionPrinter inherits functionality from two classes, demonstrating multiple inheritance.

class Printable:
    # Provides printing functionality.
    def print_document(self):
        print("Printing")

class Scannable:
    # Provides scanning functionality.
    def scan_document(self):
        print("Scanning")

# Inherits functionality from both Printable and Scannable.
class MultiFunctionPrinter(Printable, Scannable):
    pass

Since multiple inheritance can be useful in some designs, but it can also introduce complexity, particularly when parent classes define methods with the same name. Languages such as Java avoid multiple inheritance of classes but allow a class to implement multiple interfaces.

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

An interface defines a contract that implementing classes must follow, while an abstract class can define both a contract and shared implementation or state.

For example, an abstract class might provide a common log() method while requiring subclasses to implement process().

Java supports both interfaces and abstract classes, while Python can use abstract base classes to express similar designs.

I would choose an interface when different, potentially unrelated classes need to follow the same contract. I would choose an abstract class when related classes also need to share state or implementation.

39. What is the difference between method overriding and method overloading?

Method overriding occurs when a child class provides a different implementation of a method inherited from its parent. On the other hand, method overloading means defining multiple methods with the same name but different parameter lists.

In the Java code below, Calculator uses the same add() method name with different parameter lists, demonstrating method overloading in Java.

class Calculator {
    // Adds two numbers.
    int add(int a, int b) {
        return a + b;
    }

    // Adds three numbers using the same method name.
    int add(int a, int b, int c) {
        return a + b + c;
    }
}

Since Python does not support traditional method overloading like Java, defining the same method twice simply replaces the earlier definition.

40. When can inheritance lead to overly tight coupling?

Inheritance can create tight coupling when a child class depends heavily on the internal behavior or implementation details of its parent.

For example, if several subclasses depend on specific internal fields or side effects of a parent method, changing that parent method could break many subclasses.

If the subclasses mainly need a common contract rather than shared implementation, an interface or composition may be a better choice.

SOLID Principles and OOP Design Interview Questions

As you progress in system design and senior-level software engineering interviews, questions shift from basic syntax and class structures to the foundational principles of clean architecture. The SOLID principles provide a way to evaluate those decisions. These questions focus on recognizing design problems, explaining trade-offs, and improving small examples rather than memorizing definitions.

41. What is the Single Responsibility Principle (SRP)?

SRP states that a class should have one, and only one, reason to change. This means a class should be responsible for a single part of the software's functionality.

For example, the Python code below shows a class with too many responsibilities, where it generates reports, saves files, and sends emails.

class Report:
    def generate(self):
        pass

    def save_to_file(self):
        pass

    def send_email(self):
        pass

A better design would separate each class to have a clearer responsibility, as shown below.

class Report:
    def generate(self):
        pass  # Generate the report

class ReportStorage:
    def save(self, report):
        pass  # Save the report

class EmailService:
    def send(self, report):
        pass  # Send the report

42. How would you identify an SRP violation?

To identify an SRP violation, I would look for a class that handles unrelated concerns or changes for several independent reasons.

For example, if a User class validates users, writes database records, sends emails, and generates PDF reports, it likely has too many responsibilities.

I would separate those responsibilities into focused classes while keeping related behavior together.

43. What is the Open/Closed Principle (OCP)?

OCP states that software should be open for extension but closed for modification. New behavior should ideally be added without repeatedly changing stable existing code.

For example, the function below must be modified whenever a new discount type is introduced:

def calculate(discount_type, price):
    # Applies a discount based on the customer's discount type.
    if discount_type == "student":
        # Applies a 10% discount for students.
        return price * 0.9
    elif discount_type == "senior":
        # Applies a 20% discount for senior customers.
        return price * 0.8

But Polymorphism can make the design easier to extend. So, a new discount can be added as another class without changing the existing discount implementations.

class Discount:
    def apply(self, price):
        raise NotImplementedError  # Subclasses provide the behavior

class StudentDiscount(Discount):
    def apply(self, price):
        return price * 0.9  # Apply student discount

class SeniorDiscount(Discount):
    def apply(self, price):
        return price * 0.8  # Apply senior discount

44. What is the Liskov Substitution Principle (LSP)?

LSP states that objects of a child class should be usable wherever objects of the parent class are expected, without breaking expected behavior.

For example, the Python code below shows Penguin cannot properly substitute for a Bird that promises a working fly() operation.

class Bird:
    def fly(self):
        pass  # Bird promises flying behavior

class Penguin(Bird):
    def fly(self):
        # Penguin cannot satisfy the parent's contract
        raise NotImplementedError("Penguins cannot fly")

So, a better design would separate the flying behavior such that the hierarchy now better reflects the actual capabilities of the objects.

class Bird:
    pass  # Common bird behavior

class FlyingBird(Bird):
    def fly(self):
        pass  # Only flying birds need this behavior

class Penguin(Bird):
    pass  # Penguin does not inherit fly()

45. How do you identify an LSP violation?

To identify LSP violation, look for subclasses that:

  • Throw errors for operations the parent promises to support.
  • Change expected behavior in unexpected ways.
  • Require stronger conditions than the parent.
  • Return results that violate the parent's contract.

Hence, if a subclass cannot reasonably behave like the parent, the inheritance relationship may be wrong.

46. What is the Interface Segregation Principle (ISP)?

ISP states that a class should not be forced to depend on methods it does not use.

For example, the code below shows a violation of ISP because it combines printing, scanning, and faxing into one large interface. A class that only needs to print would still be forced to depend on scan() and fax().

class Machine:
    def print(self):
        pass  # Print a document

    def scan(self):
        pass  # Scan a document

    def fax(self):
        pass  # Send a fax

Instead of one large Machine interface, we use smaller interfaces so classes only depend on the methods they actually need.

class Printer:
    # Defines printing functionality.
    def print(self):
        pass

class Scanner:
    # Defines scanning functionality.
    def scan(self):
        pass

class Fax:
    # Defines faxing functionality.
    def fax(self):
        pass

47. What is the Dependency Inversion Principle (DIP)?

The Dependency Inversion Principle (DIP) states that high-level code should not be tightly dependent on specific low-level implementations. Instead, both should depend on an abstraction.

In our example below, OrderService is directly tied to GmailService. If you later want to use Outlook, SendGrid, or another email provider, you have to modify OrderService.

class OrderService:
    def __init__(self):
        # Directly creates a specific email service.
        self.email = GmailService()

You can fix this so OrderService no longer depends directly on a specific email provider and works with whatever email service is provided.

class OrderService:
    def __init__(self, email_service):
        # Receives the email service from outside.
        self.email = email_service

Python OOP Interview Questions

As we have seen in the above scenarios, OOP concepts are shared across languages, but their implementation can differ significantly. In these next sections, we will review questions that focus on language-specific features that interviewers commonly test in Python, Java, and C++, starting with Python.

48. What is self in Python?

self refers to the current object instance. It allows instance methods to access the object's attributes and other methods

In the example below, self.name refers to the name attribute belonging to the current object.

class Employee:
    def __init__(self, name):
        self.name = name  # Store data on this object

    def greet(self):
        return f"Hello, {self.name}"
`

49. What is the difference between a class method and a static method?

A class method receives the class as its first argument (cls) and can access or modify class-level data, while a static method does not receive self or cls automatically and behaves like a regular function placed inside a class.

In the example below, get_company() works with class-level data, while add() is a utility function associated with the class.

class Employee:
    company = "ABC Ltd"

    @classmethod
    def get_company(cls):
        # Accesses class-level data.
        return cls.company

    @staticmethod
    def add(a, b):
        # Performs an operation without using class or instance data.
        return a + b

50. How does multiple inheritance work in Python?

Python allows a class to inherit from multiple parent classes. Although multiple inheritance can be useful, it can also make class relationships and method lookup more difficult to understand.

The example below shows how MultiFunctionPrinter inherits methods from both Printer and Scanner.

class Printer:
    def print_document(self):
        print("Printing")

class Scanner:
    def scan_document(self):
        print("Scanning")

# Inherits functionality from both classes.
class MultiFunctionPrinter(Printer, Scanner):
    pass

51. What is method resolution order (MRO) in Python?

MRO determines the order Python follows when searching for a method or attribute in a class hierarchy. Python uses the C3 linearization algorithm to calculate this order.

You can inspect the MRO using __mro__ or mro(). It is useful when multiple inheritance is involved.

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

print(D.mro())  # Shows the method lookup order

52. What are dunder methods in Python?

Dunder, or “double underscore,” methods are special methods that let classes define how objects behave with Python's built-in operations. They include methods like __init__, __str__, __len__, __eq__, and __add__.

In the example below, __str__() controls what is returned when the object is passed to str() or printed.

# Defines the class employee
class Employee:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name  # Defines the object's string representation

employee = Employee("Alice")
print(employee)

Java OOP Interview Questions

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

An interface defines a contract that classes can implement, while an abstract class can provide shared state and implementation while also declaring abstract methods. The table below summarizes these differences:

Interface

Abstract Class

Defines a contract/capability 

Provides a shared base for related classes 

A class can implement multiple interfaces

A class can extend only one class

Useful for unrelated classes sharing behavior

Useful when classes share state or implementation

Supports abstract methods and can also contain default/static methods

Can contain abstract and concrete methods

54. What are Java access modifiers?

Access modifiers control where classes, methods, and attributes can be accessed. Java provides four main access levels:

  • public: accessible from anywhere.

  • protected: accessible within the same package and by subclasses.

  • default/package-private: accessible within the same package.

  • private: accessible only within the declaring class.

In the example below, private protects the internal state while the public method provides controlled access.

class Account {
    private double balance;  // Only Account can access this directly.
    
    public double getBalance() {
        return balance;
    }
}

55. What does final mean in Java?

final in Java is useful when you want to prevent further modification or inheritance. The meaning of final depends on where it is used. For example,

  • A final variable cannot be reassigned.
  • A final method cannot be overridden.
  • A final class cannot be extended.

For example, you cannot create a subclass that inherits from Employee in the example below.

final class Employee {
    final String id = "E001";  // Cannot be reassigned

    final void work() {
        // Cannot be overridden by a subclass
    }
}

56. What is the difference between method overloading and overriding in Java?

Method overloading occurs when a class has multiple methods with the same name but different parameter lists. In the example below, the compiler determines which overloaded method to call based on the arguments.

class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;  // Different number of parameters
    }
}

On the other hand, method overriding occurs when a subclass provides its own implementation of a method inherited from its parent class.

Below is an example where the Dog class modifies the speak() action inherited from the Animal class so that it prints “Woof” instead of “Animal sound.”

class Animal {
    void speak() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("Woof");  // Replace parent implementation
    }
}

C++ OOP Interview Questions

57. What are constructors and destructors in C++?

A constructor initializes an object when it is created, while a destructor performs cleanup when the object is destroyed to release resources.

In the example below, the constructor handles initialization, while the destructor handles cleanup.

class Car {
public:
    // Constructor runs when the object is created.
    Car() {
        cout << "Car created";
    }

    // Destructor runs when the object is destroyed.
    ~Car() {
        cout << "Car destroyed";
    }
};

58. What is a virtual function in C++?

A virtual function allows a derived class to override a method and have the correct implementation selected at runtime when accessed through a base-class pointer or reference

For example, a Dog object can provide its own speak() behavior even when accessed through an `Animal reference or pointer.

class Animal {
public:
    // Allows subclasses to provide their own implementation.
    virtual void speak() {
        cout << "Some sound";
    }
};

class Dog : public Animal {
public:
    void speak() override {
        cout << "Woof";
    }
};

59. Does C++ support multiple inheritance?

Yes. A C++ class can inherit from multiple base classes. 

Below is an example where MultiFunctionPrinter inherits functionality from both Printer and Scanner.

class Printer {
public:
    void print() {
        cout << "Printing";
    }
};

class Scanner {
public:
    void scan() {
        cout << "Scanning";
    }
};

// Inherits from both base classes.
class MultiFunctionPrinter : public Printer, public Scanner {
};

60. What are access specifiers in C++?

C++ uses public, protected, and private to control access to class members. The example below shows how the access controls are implemented.

class Employee {
private:
    double salary;  // Only Employee can access it

protected:
    int id;         // Employee and derived classes can access it

public:
    void work() {}  // Accessible from outside
};

OOP Coding Interview Questions

Let’s now explore questions that assess whether candidates can apply OOP principles to practical programming problems.

61. Design a BankAccount class

First, create a BankAccount class that supports deposits, withdrawals, and balance checking. Reject invalid amounts and withdrawals that exceed the balance.

Below is an example implementation in Python.

class BankAccount:
    def __init__(self, account_number, balance=0):
        self.account_number = account_number
        self._balance = balance  # Stores the account balance.

    def deposit(self, amount):
        # Accepts only positive deposits.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self._balance += amount

    def withdraw(self, amount):
        # Validates the withdrawal before updating the balance.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        if amount > self._balance:
            raise ValueError("Insufficient funds")
        self._balance -= amount

    def get_balance(self):
        # Returns the current balance.
        return self._balance

62. Model employees with different compensation rules

Design an employee system where full-time employees receive a fixed salary, while commission-based employees receive a base salary plus commission.

Avoid putting every compensation rule into one large conditional statement. The example below shows the implementation of the above scenario in Python.

class Employee:
    def calculate_pay(self):
        raise NotImplementedError  # Subclasses define compensation rules

class FullTimeEmployee(Employee):
    def __init__(self, salary):
        self.salary = salary

    def calculate_pay(self):
        return self.salary  # Fixed compensation

class CommissionEmployee(Employee):
    def __init__(self, salary, commission):
        self.salary = salary
        self.commission = commission

    def calculate_pay(self):
        return self.salary + self.commission  # Base plus commission

63. Implement a shape hierarchy

Start by creating a Shape abstraction with area() behavior. Implement Circle and Rectangle without changing the code that calculates the total area.

Below is an example implementation in Python.

from math import pi

class Shape:
    def area(self):
        raise NotImplementedError  # Each shape provides its own calculation

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return pi * self.radius ** 2  # Circle area

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height  # Rectangle area

def total_area(shapes):
    return sum(shape.area() for shape in shapes)  # Polymorphic calls

64. Refactor duplicated classes using inheritance or composition

Suppose Car and Truck both contain duplicated start_engine() logic. How would you refactor the design?

The best solution to refactor the design is to extract the shared behavior.

class Vehicle:
    def start_engine(self):
        # Provides shared engine-starting behavior.
        print("Engine started")

class Car(Vehicle):
    pass

class Truck(Vehicle):
    pass

65. Implement polymorphic behavior

Create different payment types that can be processed through the same interface.

The example below, in Python, uses polymorphism to loop through a list of different payment types and execute the correct process method for each one.

# Define a class for card payment processing
class CardPayment:
    def process(self, amount):
        print(f"Processing card payment: {amount}")

# Define a class for cash payment processing
class CashPayment:
    def process(self, amount):
        print(f"Processing cash payment: {amount}")

# Create a list containing different payment object instances
payments = [CardPayment(), CashPayment()]

# Loop through each payment object in the list
for payment in payments:
    # Execute the process method dynamically based on the object type (Polymorphism)
    payment.process(100)

66. Identify problems in an existing class design

Review the class shown below and identify the possible issues.

class User:
    def validate_email(self):
        pass

    def save_to_database(self):
        pass

    def send_welcome_email(self):
        pass

    def generate_report(self):
        pass

The class has several unrelated responsibilities, including user validation, database persistence, email delivery, and report generation.

This is likely an SRP violation, hence I would separate these responsibilities into appropriate components for each method as shown below.

class UserValidator:
    def validate_email(self, email):
        pass  # Validate user data

class UserRepository:
    def save(self, user):
        pass  # Handle persistence

class EmailService:
    def send_welcome(self, user):
        pass  # Handle email delivery

class ReportService:
    def generate(self, user):
        pass  # Handle reporting

Scenario-Based OOP Interview Questions

The questions in this section test design judgment rather than whether you can identify one supposedly correct pattern. A strong answer should explain the reasoning, alternatives, and trade-offs involved.

67. When would you choose composition over inheritance?

I would prefer a composition where an object has or uses another object rather than being a specialized version of it. However, I would choose inheritance if there is a stable is-a relationship, and the child should satisfy the parent's contract.

Composition usually provides greater flexibility, while inheritance can provide simple reuse when the class hierarchy is stable and conceptually appropriate.

68. How would you refactor a class that has too many responsibilities?

First, I would identify the different reasons the class might change. I would then group related responsibilities and move unrelated behavior into separate classes.

For example, if an Order class validates orders, calculates prices, saves data, and sends emails, I might separate those concerns into an OrderValidator, PricingService, OrderRepository, and EmailService.

I would avoid splitting the class mechanically. Too many tiny classes can make a simple system complex.

69. How would you add new behavior without modifying existing classes?

If the behavior is expected to vary, I might define an abstraction and provide separate implementations.

For example, the implementation below adds a new payment method as a class.

class PaymentMethod:
    def pay(self, amount):
        raise NotImplementedError  # Define the payment contract

class CardPayment(PaymentMethod):
    def pay(self, amount):
        print(f"Card payment: {amount}")  # Card implementation

class MobilePayment(PaymentMethod):
    def pay(self, amount):
        print(f"Mobile payment: {amount}")  # Mobile implementation

I would not introduce an abstraction simply because more behavior might be added someday. It becomes more valuable when new implementations are expected.

70. How would you design a notification system supporting email, SMS, and push notifications?

I would start with a common notification interface and separate implementations: The expected likelihood of change matters. Below is an implementation in Python.

class Notification:
    def send(self, message):
        raise NotImplementedError  # Common contract

class EmailNotification(Notification):
    def send(self, message):
        print(f"Email: {message}")  # Email delivery

class SMSNotification(Notification):
    def send(self, message):
        print(f"SMS: {message}")  # SMS delivery

class PushNotification(Notification):
    def send(self, message):
        print(f"Push: {message}")  # Push delivery

A service could receive a notification implementation through dependency injection:

class NotificationService:
    def __init__(self, notification):
        # Receives the notification implementation from outside.
        self.notification = notification

    def send(self, message):
        self.notification.send(message)

This design supports polymorphism and reduces coupling between the service and specific delivery mechanisms.

However, in a larger system, I might use a factory, dependency-injection framework, or configuration-based approach to select notification channels. The best choice depends on the application's size and requirements.

71. How would you reduce tight coupling between two classes?

I would first identify why the classes are tightly coupled. If one class directly creates or depends on a concrete implementation, I could introduce dependency injection or an abstraction.

Instead of:

class OrderService:
    def __init__(self):
        self.payment = StripePayment()  # Concrete dependency

I would inject the dependency so that OrderService can work with different payment implementations.

class OrderService:
    def __init__(self, payment):
        self.payment = payment  # Dependency supplied externally

However, abstraction is not automatically better. If the dependency is simple, stable, and unlikely to vary, adding an interface may add unnecessary complexity.

Advanced OOP Interview Questions

If you are an experienced developer, the questions below move beyond basic OOP definitions. They test whether you can reason about architecture, language behavior, maintainability, and trade-offs, including recognizing situations where OOP may not be the best approach.

72. What is Dependency Injection, and why does it matter?

Dependency injection means providing an object with the dependencies it needs rather than having it create those dependencies itself. This reduces coupling and makes components easier to test and replace.

Below is an example where dependency injection passes a payment service into the OrderService when it is created, so it can handle checkouts without hardcoding a specific payment method.

class OrderService:
    def __init__(self, payment_service):
        # Receives the dependency from outside.
        self.payment_service = payment_service

    def checkout(self, amount):
        self.payment_service.process(amount)

73. What is the difference between coupling and cohesion?

Coupling describes how strongly components depend on each other, while cohesion describes how closely related the responsibilities within a component are.

A good design generally aims for low coupling and high cohesion.

For example, a class that only handles invoice calculations has high cohesion. If it also sends emails and manages database connections, its cohesion decreases.

Similarly, a class that directly creates and controls many unrelated services has high coupling.

74. What is the difference between object identity and object equality?

Identity asks whether two references point to the same object, while equality asks whether two objects should be considered equivalent based on their values or state.

The example below shows how Python evaluates both is and == based on memory location for custom object instances. To make == compare the actual user_id values instead of memory addresses, you must explicitly implement the __eq__ magic method inside the class.

# Define a User class with an ID attribute
class User:
    def __init__(self, user_id):
        self.user_id = user_id

# Create two separate instances with identical ID data
user1 = User(101)
user2 = User(101)

# Outputs False 
# They are distinct objects located at different memory addresses
print(user1 is user2)  

# Outputs False 
# Custom classes require a custom __eq__ method to compare values
print(user1 == user2)  

75. Why is immutability useful in object-oriented design?

An immutable object cannot change after creation. This reduces unexpected side effects because its state cannot be modified by another part of the program.

For example, the code below uses the add method to preserve the original data of both objects. It produces a fresh Money instance containing the sum.

class Money:
    # Initialize the money object with a value and currency type
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency

    # Combine values without altering the original instances
    def add(self, other):
        # Return a new object instead of changing this one
        return Money(self.amount + other.amount, self.currency)

Immutability can make objects easier to reason about and safer to share, particularly in concurrent programs. However, creating new objects can sometimes increase memory use or allocation overhead.

76. What is dynamic dispatch?

Dynamic dispatch means the implementation of a method is selected at runtime based on the actual object rather than simply the declared type of the reference.

In the example below, the parent class Animal requires any subclass to define a speak() method; otherwise, Python will raise an error when the method is called.

# Base class acting as an abstract interface
class Animal:
    # Forces subclasses to implement this method
    def speak(self):
        raise NotImplementedError

# Subclass providing a specific dog behavior
class Dog(Animal):
    def speak(self):
        return "Woof"  # Dog implementation

# Subclass providing a specific cat behavior
class Cat(Animal):
    def speak(self):
        return "Meow"  # Cat implementation

# Create a collection of different animal objects
animals = [Dog(), Cat()]

# Iterate through the collection uniformly
for animal in animals:
    # Automatically triggers the correct subclass method at runtime
    print(animal.speak())  # Resolved according to the actual object

77. What are covariance and contravariance?

Variance describes how compatible generic or function types are when their related types have an inheritance relationship. Therefore:

  • Covariance: Allows a more specific type to substitute for a more general type in an output/producer position.
  • Contravariance: Allows a more general type to be used where a more specific type is expected in an input/consumer position.
  • Invariance: Allows neither substitution.

A simple way to remember the distinction is: Producers tend to be covariant, while consumers tend to be contravariant.

The exact rules depend on the language's type system. For example, languages such as C# and Java provide explicit variance mechanisms for certain generic types, while Python's typing system supports concepts such as TypeVar variance and protocols.

78. What object-oriented design trade-offs should you consider?

I would consider several factors rather than automatically applying a particular pattern, including:

  • Flexibility versus simplicity: More abstractions can make future changes easier, but can also make simple code harder to understand.
  • Reuse versus coupling: Inheritance can reuse code, but may tightly couple subclasses to a parent.
  • Encapsulation versus accessibility: Hiding implementation details can improve safety but may require additional interfaces or methods.
  • Performance versus abstraction: Indirection can sometimes introduce runtime or memory costs.
  • Testability versus complexity: Dependency injection can improve testing but may add configuration.
  • Inheritance versus composition: The domain relationship and expected changes should determine the choice.

79. When can dependency injection become over-engineering?

Dependency injection can become excessive when every small class receives several abstractions even though its dependencies are simple, stable, and unlikely to change.

For example, creating an interface and dependency-injection configuration for a class that performs a simple calculation may add complexity without providing much value.

I would use dependency injection when replacing a dependency, testing it independently, or controlling its lifecycle.

80. What are some limitations of object-oriented programming?

While OOP is useful for modeling systems around objects and behavior, it has some limitations, which include the following:

  • Large inheritance hierarchies can become difficult to maintain.
  • Excessive abstraction can make simple problems unnecessarily complex.
  • Objects can introduce mutable state and unexpected side effects.
  • Small operations may require many interacting objects.
  • Object-oriented designs can sometimes obscure straightforward data transformations.

Common OOP Interview Mistakes

Even candidates who know OOP concepts can struggle when interview questions move from definitions to practical design. The following are the common mistakes to avoid when answering different questions:

  • Memorizing the four pillars without applying them: Don't just name encapsulation, abstraction, inheritance, and polymorphism. Explain how you would use each in a real design.
  • Confusing abstraction and encapsulation: Remember that abstraction focuses on what an object exposes, while encapsulation focuses on bundling and controlling access to its data and behavior.
  • Assuming inheritance is always better than composition: As a rule, only use inheritance for a genuine is-a relationship and use composition when an object has or uses another object.
  • Confusing overloading and overriding: Overloading uses the same method name with different parameters, while overriding replaces inherited behavior in a subclass.
  • Creating unnecessary class hierarchies: Don't introduce inheritance simply to reuse a few lines of code. Consider composition or simpler functions first.
  • Focusing on syntax instead of design: Interviewers often care more about why you chose a particular structure than whether you remember every language-specific syntax detail.

How to Prepare for an OOP Interview

From my experience as a developer, I recommend you use the following approach when preparing for your next OOP interview:

  • Practice the four OOP principles: Explain each principle in your own words and give a simple example.
  • Design small class hierarchies: Practice modeling systems such as employees, vehicles, payments, or shapes.
  • Compare inheritance and composition: For each design, explain why one approach is more appropriate and what trade-offs it introduces.
  • Review SOLID principles: Practice identifying SOLID violations and suggesting reasonable improvements.
  • Solve OOP coding exercises: Implement classes, interfaces, polymorphic behavior, and small object-oriented systems.
  • Study your primary language's OOP features: Know how your language handles constructors, access control, inheritance, interfaces, overriding, static members, and other OOP features.

Conclusion

The strongest candidates explain why they made a particular design choice, recognize its trade-offs, and consider alternative approaches. To that end, to prepare effectively, practice designing and refactoring small systems, identifying design problems, and implementing concepts such as encapsulation, abstraction, inheritance, and polymorphism.

Now you're probably ready for a structured skill track: Our Building Applications with OOP in Python track is also helpful if you want to master the fundamentals of object-oriented programming, including type hinting, abstract base classes, and interfaces. Also, our Java Developer career track is most helpful if you use Java in building applications and need to understand more of its syntax in OOP.


Allan Ouko's photo
Author
Allan Ouko
LinkedIn

Data Science Technical Writer with hands-on experience in data analytics, business intelligence, and data science. I write practical, industry-focused content on SQL, Python, Power BI, Databricks, and data engineering, grounded in real-world analytics work. My writing bridges technical depth and business impact, helping professionals turn data into confident decisions. 

FAQs

What topics should I study for an OOP interview?

Focus on classes and objects, the four OOP principles, inheritance, composition, polymorphism, abstraction, encapsulation, interfaces, SOLID principles, and practical object design.

What OOP coding problems should I practice?

Practice designing systems such as bank accounts, employee compensation models, payment systems, notification systems, and shape hierarchies.

How should I answer OOP design questions?

Explain your reasoning. Identify responsibilities, relationships, dependencies, and trade-offs before describing the implementation.

Do OOP interview questions differ by programming language?

Yes. Core concepts are similar, but implementation differs. For example, Python has dunder methods and MRO, Java has interfaces and final, while C++ has destructors and virtual functions.

How should I prepare for an OOP interview?

Review the core concepts, practice small coding exercises, design and refactor class hierarchies, compare inheritance with composition, and study OOP features specific to your primary language.

주제
Python

Learn Object-Oriented Programming with DataCamp

courses

Python 중급 객체 지향 프로그래밍

4
9.6K
descriptors, 다중 상속, 추상 기본 클래스로 OOP 역량을 탄탄히 구축하세요!
자세히 보기Right Arrow
강좌 시작
더 보기Right Arrow
관련된

blogs

The 41 Top Python Interview Questions & Answers For 2026

Master 41 Python interview questions for 2026 with code examples. Covers basics, OOP, data science, AI/ML, and FAANG-style coding challenges.
Abid Ali Awan's photo

Abid Ali Awan

15분

blogs

Introduction to Programming Paradigms

Explore the core concepts of major programming paradigms with Python examples, including object-oriented, functional, procedural, and declarative paradigms.
Samuel Shaibu's photo

Samuel Shaibu

12분

blogs

Top 24 Programming Interview Questions For 2026

Discover essential programming interview questions with Python examples for job seekers, final-year students, and data professionals.
Javier Canales Luna's photo

Javier Canales Luna

14분

tutorials

Object-Oriented Programming in Python: A Complete Guide

Learn the basics of object-oriented programming in Python: classes, objects, attributes, and methods explained step by step with code examples.
Théo Vanderheyden's photo

Théo Vanderheyden

12분

tutorials

OOP in Java: Classes, Objects, Encapsulation, Inheritance and Abstraction

Learn Object-Oriented Programming in Java with practical examples. Master classes, objects, inheritance, encapsulation, and abstract classes using a restaurant menu system.
Bex Tuychiev's photo

Bex Tuychiev

15분

tutorials

Encapsulation in Python: A Comprehensive Guide

Learn the fundamentals of implementing encapsulation in Python object-oriented programming.
Bex Tuychiev's photo

Bex Tuychiev

11분

더 보기더 보기