Oop Concepts Questions 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
| Pillar | Definition | Purpose |
|---|---|---|
| Encapsulation | Bundling data and methods that operate on data | Data hiding, controlled access |
| Inheritance | Deriving new class from existing class | Code reuse, hierarchical relationships |
| Polymorphism | Same interface, different implementations | Flexibility, extensibility |
| Abstraction | Hiding implementation details | Simplification, 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
| Modifier | Same Class | Same Package | Subclass | Everywhere |
|---|---|---|---|---|
| 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]
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Can have abstract and concrete methods | Traditionally only abstract (Java 8+ allows default) |
| Variables | Can have instance variables | Only static final constants |
| Constructor | Can have constructors | No constructors |
| Inheritance | Single inheritance | Multiple inheritance allowed |
| Access Modifiers | Any | Public 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]
| Aspect | Method Overloading | Method Overriding |
|---|---|---|
| Definition | Same method name, different parameters | Same method signature in subclass |
| Class | Same class | Parent and child classes |
| Parameters | Must be different | Must be same |
| Return Type | Can be different | Must be same (or covariant) |
| Binding | Compile-time (static) | Runtime (dynamic) |
| Inheritance | Not required | Required |
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]
| Feature | Static | Instance |
|---|---|---|
| Belongs to | Class | Object |
| Memory | One copy for all objects | Separate copy per object |
| Access | ClassName.member or object | Only through object |
| Lifetime | Program duration | Object lifetime |
| Usage | Shared data, utilities | Object-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
- "Diamond problem in inheritance?" → Ambiguity when class inherits from two classes with same method. Solved by MRO in Python, interfaces in Java.
- "Deep copy vs shallow copy?" → Shallow copies references, deep copies values recursively.
- "str vs repr in Python?" → str for readable, repr for unambiguous/developer.
- "Metaclasses in Python?" → Classes that create classes, control class creation.
- "Duck typing?" → "If it walks like a duck..." - type determined by behavior not inheritance.
Companies Asking OOP Questions
| Company | Frequency | Focus Areas |
|---|---|---|
| Amazon | Very High | SOLID principles, Design patterns |
| Microsoft | Very High | C++ OOP, Virtual functions |
| High | System design with OOP | |
| Oracle | Very High | Java OOP, Inheritance nuances |
| Adobe | High | Design patterns, Architecture |
| SAP | High | Complex class hierarchies |
Preparation Tips
- Practice Design: Design real-world systems using OOP
- Know Your Language: OOP implementation varies (Python vs Java vs C++)
- SOLID Principles: Understand and apply all five
- Design Patterns: Know Singleton, Factory, Observer, Strategy at minimum
- Code Examples: Have ready examples for all concepts
- UML Diagrams: Practice class diagrams
- 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! 🏗️