PlacementPrep

Oop Concepts Questions Placement

28 min read
Topics & Practice
Advertisement Placement

OOP Concepts Interview Questions and Answers for Placements

Last Updated: March 2026

Introduction to Object-Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects" that contain data and code. Understanding OOP principles is fundamental for software engineering interviews as it forms the basis of modern software design and development.

Why OOP is Critical for Placements

  • Universal Paradigm: Used in Java, C++, Python, C#, JavaScript
  • Design Foundation: Basis for system design and architecture interviews
  • Code Organization: Essential for writing maintainable, scalable code
  • Industry Standard: Every major company expects strong OOP knowledge

Key Concepts and Theory

Four Pillars of OOP

PillarDefinitionPurpose
EncapsulationBundling data and methods that operate on dataData hiding, controlled access
InheritanceDeriving new class from existing classCode reuse, hierarchical relationships
PolymorphismSame interface, different implementationsFlexibility, extensibility
AbstractionHiding implementation detailsSimplification, focus on what not how

Class vs Object

# Class is a blueprint
class Car:
    def __init__(self, brand, model):
        self.brand = brand  # Attribute
        self.model = model
    
    def drive(self):  # Method
        print(f"Driving {self.brand} {self.model}")

# Object is an instance
my_car = Car("Toyota", "Camry")  # Object creation
my_car.drive()  # Method call

Access Modifiers

ModifierSame ClassSame PackageSubclassEverywhere
Private
Default
Protected
Public

Practice Questions with Solutions

Question 1: Explain the Four Pillars of OOP with examples. ⭐⭐ [Medium]

1. Encapsulation: Bundling data and methods together while restricting direct access to some components.

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # Private attribute (name mangling)
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance
    
    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return True
        return False

# Usage
account = BankAccount("John", 1000)
account.deposit(500)
print(account.get_balance())  # 1500
# print(account.__balance)  # Error! Private access

2. Inheritance: Creating new classes from existing ones, inheriting attributes and methods.

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        pass

class Dog(Animal):  # Inherits from Animal
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak())  # Buddy says Woof!
print(cat.speak())  # Whiskers says Meow!

3. Polymorphism: Same interface, different implementations. Objects of different classes treated uniformly.

# Same function works with different types
def animal_concert(animals):
    for animal in animals:
        print(animal.speak())  # Polymorphic call

animals = [Dog("Rex"), Cat("Mittens"), Dog("Max")]
animal_concert(animals)

# Operator overloading
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):  # Overloading + operator
        return Vector(self.x + other.x, self.y + other.y)
    
    def __str__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)  # Vector(4, 6)

4. Abstraction: Hiding complex implementation details, showing only essential features.

from abc import ABC, abstractmethod

class Shape(ABC):  # Abstract base class
    @abstractmethod
    def area(self):
        pass
    
    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

# shape = Shape()  # Error! Can't instantiate abstract class
rect = Rectangle(5, 3)
print(rect.area())  # 15

Question 2: Difference between Abstract Class and Interface? ⭐⭐⭐ [Hard]

FeatureAbstract ClassInterface
MethodsCan have abstract and concrete methodsTraditionally only abstract (Java 8+ allows default)
VariablesCan have instance variablesOnly static final constants
ConstructorCan have constructorsNo constructors
InheritanceSingle inheritanceMultiple inheritance allowed
Access ModifiersAnyPublic by default
Use Case"Is-a" relationship with shared code"Can-do" capability contracts

Python Example (using ABC module):

from abc import ABC, abstractmethod

# Abstract Class
class Vehicle(ABC):
    def __init__(self, brand):
        self.brand = brand
    
    @abstractmethod
    def move(self):
        pass
    
    def honk(self):  # Concrete method
        print("Beep beep!")

# Interface-like (pure abstract)
class Flyable(ABC):
    @abstractmethod
    def fly(self):
        pass
    
    @abstractmethod
    def land(self):
        pass

# Multiple inheritance (Interface-like)
class FlyingCar(Vehicle, Flyable):
    def move(self):
        print(f"{self.brand} driving on road")
    
    def fly(self):
        print(f"{self.brand} flying in air")
    
    def land(self):
        print(f"{self.brand} landing")

fc = FlyingCar("AeroMobile")
fc.move()  # AeroMobile driving on road
fc.fly()   # AeroMobile flying in air

Java Example:

// Abstract Class
abstract class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    abstract void makeSound();  // Abstract method
    
    void sleep() {  // Concrete method
        System.out.println(name + " is sleeping");
    }
}

// Interface
interface Swimmable {
    void swim();
    void dive();
}

// Implementation
class Dolphin extends Animal implements Swimmable {
    public Dolphin(String name) {
        super(name);
    }
    
    @Override
    void makeSound() {
        System.out.println("Click click!");
    }
    
    @Override
    public void swim() {
        System.out.println(name + " is swimming");
    }
    
    @Override
    public void dive() {
        System.out.println(name + " is diving");
    }
}

Question 3: Explain Method Overloading vs Method Overriding. ⭐⭐ [Medium]

AspectMethod OverloadingMethod Overriding
DefinitionSame method name, different parametersSame method signature in subclass
ClassSame classParent and child classes
ParametersMust be differentMust be same
Return TypeCan be differentMust be same (or covariant)
BindingCompile-time (static)Runtime (dynamic)
InheritanceNot requiredRequired

Method Overloading Example:

class Calculator:
    # Same method name, different parameters
    def add(self, a, b):
        return a + b
    
    def add(self, a, b, c):  # Overloaded
        return a + b + c
    
    def add(self, a, b, c=0):  # Python way (default parameters)
        return a + b + c
    
    def add(self, *args):  # Variable arguments
        return sum(args)

# Python doesn't support true overloading (last definition wins)
# Use *args, **kwargs, or default parameters

# Java supports true overloading
class Calculator {
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }
    int add(int a, int b, int c) { return a + b + c; }
}

Method Overriding Example:

class Animal:
    def speak(self):
        print("Animal speaks")
    
    def move(self):
        print("Animal moves")

class Dog(Animal):
    def speak(self):  # Overriding
        print("Dog barks")
    
    def move(self):  # Overriding with super call
        super().move()  # Call parent's method
        print("Dog runs")

dog = Dog()
dog.speak()  # Dog barks (runtime polymorphism)
dog.move()
# Output: Animal moves
#         Dog runs

Question 4: What is Constructor Chaining and super() keyword? ⭐⭐ [Medium]

Constructor Chaining: Calling one constructor from another to avoid code duplication.

class Employee:
    def __init__(self, name, emp_id):
        self.name = name
        self.emp_id = emp_id
        self.department = "General"
    
    def display(self):
        print(f"{self.name} ({self.emp_id}) - {self.department}")

class Manager(Employee):
    def __init__(self, name, emp_id, team_size):
        super().__init__(name, emp_id)  # Call parent constructor
        self.team_size = team_size
        self.department = "Management"
    
    def display(self):
        super().display()  # Call parent method
        print(f"Manages {self.team_size} people")

mgr = Manager("Alice", "M001", 10)
mgr.display()

super() Usage:

class A:
    def __init__(self):
        print("A's constructor")
    
    def method(self):
        print("A's method")

class B(A):
    def __init__(self):
        super().__init__()  # Explicit parent call
        print("B's constructor")
    
    def method(self):
        super().method()  # Call parent's version
        print("B's method")

class C(B):
    def __init__(self):
        super().__init__()  # Calls B's __init__, which calls A's
        print("C's constructor")

# Method Resolution Order (MRO)
print(C.__mro__)  # Shows inheritance chain

Constructor Chaining with this() (Java-style):

class Rectangle {
    int width, height;
    
    // Default constructor
    Rectangle() {
        this(0, 0);  // Chaining to parameterized constructor
    }
    
    // Parameterized constructor
    Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    
    // Copy constructor
    Rectangle(Rectangle other) {
        this(other.width, other.height);
    }
}

Question 5: Explain Static vs Instance members. ⭐ [Easy]

FeatureStaticInstance
Belongs toClassObject
MemoryOne copy for all objectsSeparate copy per object
AccessClassName.member or objectOnly through object
LifetimeProgram durationObject lifetime
UsageShared data, utilitiesObject-specific data
class Student:
    # Class variable (static)
    school_name = "ABC High School"
    student_count = 0
    
    def __init__(self, name, roll_no):
        # Instance variables
        self.name = name
        self.roll_no = roll_no
        Student.student_count += 1  # Access static via class
    
    # Instance method
    def display_info(self):
        print(f"{self.name} - {self.roll_no}")
        print(f"School: {self.school_name}")
    
    # Class method (static-like)
    @classmethod
    def get_school(cls):
        return cls.school_name
    
    # Static method
    @staticmethod
    def is_valid_roll(roll_no):
        return isinstance(roll_no, int) and roll_no > 0

# Usage
s1 = Student("Alice", 101)
s2 = Student("Bob", 102)

print(Student.student_count)  # 2 (shared)
print(s1.school_name)  # ABC High School
print(Student.get_school())  # ABC High School
print(Student.is_valid_roll(101))  # True

Question 6: What is Composition vs Inheritance? ⭐⭐⭐ [Hard]

Inheritance ("Is-a" relationship):

  • Child class is a type of parent class
  • Strong coupling between classes
  • Can lead to fragile base class problem

Composition ("Has-a" relationship):

  • Class contains instances of other classes
  • Loosely coupled, more flexible
  • Preferred in most modern design patterns

Comparison:

# INHERITANCE (Car IS-A Vehicle)
class Vehicle:
    def move(self):
        print("Vehicle moves")

class Car(Vehicle):  # Car IS-A Vehicle
    def __init__(self):
        self.wheels = 4
    
    def honk(self):
        print("Beep!")

# COMPOSITION (Car HAS-A Engine)
class Engine:
    def start(self):
        print("Engine started")
    
    def stop(self):
        print("Engine stopped")

class Wheels:
    def rotate(self):
        print("Wheels rotating")

class Car:  # Car HAS-A Engine and Wheels
    def __init__(self):
        self.engine = Engine()  # Composition
        self.wheels = Wheels()  # Composition
    
    def start(self):
        self.engine.start()
    
    def drive(self):
        self.wheels.rotate()

# Usage
car = Car()
car.start()   # Delegated to engine
car.drive()   # Delegated to wheels

When to Use What:

Use Inheritance when:

  • True "is-a" relationship (Dog is an Animal)
  • Code reuse with same interface
  • Polymorphism needed

Use Composition when:

  • "Has-a" or "uses-a" relationship
  • Flexibility to change behavior at runtime
  • Avoiding tight coupling
  • Multiple behaviors needed (can compose multiple objects)

Design Principle: "Favor composition over inheritance"


Question 7: Explain SOLID Principles. ⭐⭐⭐ [Hard]

S - Single Responsibility Principle (SRP): A class should have only one reason to change.

# BAD: Multiple responsibilities
class Employee:
    def calculate_pay(self): pass
    def save_to_database(self): pass  # Wrong!
    def generate_report(self): pass   # Wrong!

# GOOD: Separate concerns
class Employee:
    def calculate_pay(self): pass

class EmployeeRepository:
    def save(self, employee): pass

class EmployeeReport:
    def generate(self, employee): pass

O - Open/Closed Principle (OCP): Open for extension, closed for modification.

# BAD: Modify existing code for new shapes
class AreaCalculator:
    def calculate(self, shape):
        if isinstance(shape, Rectangle):
            return shape.width * shape.height
        # Must modify for Circle!

# GOOD: Extend via new classes
class Shape(ABC):
    @abstractmethod
    def area(self): pass

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

class Circle(Shape):
    def area(self): return 3.14 * self.radius ** 2

L - Liskov Substitution Principle (LSP): Subtypes must be substitutable for base types.

# BAD: Square breaks Rectangle contract
class Rectangle:
    def set_width(self, w): self.width = w
    def set_height(self, h): self.height = h
    def area(self): return self.width * self.height

class Square(Rectangle):  # Violates LSP!
    def set_width(self, w):
        self.width = self.height = w
    # Now set_height breaks area calculation

# GOOD: Separate hierarchies or composition

I - Interface Segregation Principle (ISP): Clients shouldn't depend on interfaces they don't use.

# BAD: Fat interface
class Worker(ABC):
    @abstractmethod
    def work(self): pass
    @abstractmethod
    def eat(self): pass  # Robots don't eat!

# GOOD: Segregated interfaces
class Workable(ABC):
    @abstractmethod
    def work(self): pass

class Eatable(ABC):
    @abstractmethod
    def eat(self): pass

class Human(Workable, Eatable): pass
class Robot(Workable): pass

D - Dependency Inversion Principle (DIP): Depend on abstractions, not concretions.

# BAD: High-level depends on low-level
class MySQLDatabase:
    def connect(self): pass

class Application:
    def __init__(self):
        self.db = MySQLDatabase()  # Concrete dependency

# GOOD: Both depend on abstraction
class Database(ABC):
    @abstractmethod
    def connect(self): pass

class MySQLDatabase(Database): pass
class PostgreSQLDatabase(Database): pass

class Application:
    def __init__(self, db: Database):  # Dependency injection
        self.db = db

Question 8: What are Design Patterns? Explain common ones. ⭐⭐⭐ [Hard]

Design Patterns are reusable solutions to commonly occurring problems in software design.

Creational Patterns:

Singleton: One instance only

class Singleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

Factory: Create objects without specifying exact class

class AnimalFactory:
    @staticmethod
    def create_animal(animal_type):
        if animal_type == "dog":
            return Dog()
        elif animal_type == "cat":
            return Cat()

Structural Patterns:

Adapter: Convert interface to another

class EuropeanSocket:
    def voltage(self): return 230

class USSocket:
    def voltage(self): return 120

class Adapter:
    def __init__(self, european_socket):
        self.socket = european_socket
    
    def voltage(self):  # Adapt to US standard
        return self.socket.voltage()

Decorator: Add responsibilities dynamically

class Coffee:
    def cost(self): return 5

class MilkDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    def cost(self): return self.coffee.cost() + 1

Behavioral Patterns:

Observer: One-to-many dependency

class Subject:
    def __init__(self):
        self._observers = []
    
    def attach(self, observer):
        self._observers.append(observer)
    
    def notify(self):
        for observer in self._observers:
            observer.update()

Strategy: Family of interchangeable algorithms

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount): pass

class CreditCardPayment(PaymentStrategy):
    def pay(self, amount): print(f"Paid {amount} via Credit Card")

class PayPalPayment(PaymentStrategy):
    def pay(self, amount): print(f"Paid {amount} via PayPal")

Common Interview Follow-Up Questions

  1. "Diamond problem in inheritance?" → Ambiguity when class inherits from two classes with same method. Solved by MRO in Python, interfaces in Java.
  2. "Deep copy vs shallow copy?" → Shallow copies references, deep copies values recursively.
  3. "str vs repr in Python?"str for readable, repr for unambiguous/developer.
  4. "Metaclasses in Python?" → Classes that create classes, control class creation.
  5. "Duck typing?" → "If it walks like a duck..." - type determined by behavior not inheritance.

Companies Asking OOP Questions

CompanyFrequencyFocus Areas
AmazonVery HighSOLID principles, Design patterns
MicrosoftVery HighC++ OOP, Virtual functions
GoogleHighSystem design with OOP
OracleVery HighJava OOP, Inheritance nuances
AdobeHighDesign patterns, Architecture
SAPHighComplex class hierarchies

Preparation Tips

  1. Practice Design: Design real-world systems using OOP
  2. Know Your Language: OOP implementation varies (Python vs Java vs C++)
  3. SOLID Principles: Understand and apply all five
  4. Design Patterns: Know Singleton, Factory, Observer, Strategy at minimum
  5. Code Examples: Have ready examples for all concepts
  6. UML Diagrams: Practice class diagrams
  7. Refactoring: Know how to improve poorly designed code

FAQ

Q: Can you override static methods?
A: No, static methods belong to class not instance. You can hide them in subclass but not override.

Q: What's the difference between == and equals() in Java?
A: == compares references (memory addresses), equals() compares content. Override equals() for value comparison.

Q: Why use abstract classes instead of interfaces?
A: Abstract classes when you need shared code/fields, interfaces for pure contracts. Java 8+ blurs this with default methods.

Q: What is method hiding?
A: When subclass defines static method with same signature as parent. Not overriding - no polymorphism.

Q: Explain final, finally, finalize.
A: final (keyword - immutable/unchangeable), finally (block - always executes), finalize (method - called before GC).


Master OOP and design scalable, maintainable systems! 🏗️

Advertisement Placement

Share this article: