05
JanExploring the Core Features of Object-Oriented Programming(OOP)
Features of OOPs
The features of Object-Oriented Programming (OOPs) form the foundation of modern programming languages and methodologies. These features empower developers to design software that is modular, reusable, and easy to maintain. In today's fast-paced technological world, features of OOPs are critical for building scalable and robust applications. They enhance collaboration among developers, as code can be organized into self-contained units that are easier to understand and debug.
In the OOPs tutorial, we will learn some exciting concepts related Object Oriented Programming, including object-oriented programming features, encapsulation, polymorphism, inheritance, abstraction, dynamic binding, message binding, and a lot more.
What is Object-Oriented Programming?
Object-oriented programming (OOP) is a way of designing programs by organizing data and actions into objects that are based on real-world things. Each object has attributes (data) and behaviors (methods) that define how it works. OOP makes it easier to model, understand, and solve real-world problems in programming.
Core Features of OOP
The core features of OOP serve as the building blocks of this programming paradigm. They are designed to make software development easier, more efficient, and more organized. These features include:
- Encapsulation: Bundling data and methods into a single unit to safeguard sensitive information.
- Inheritance: Enabling code reusability by allowing new classes to inherit from existing ones.
- Polymorphism: Allowing objects to behave differently based on their context.
- Abstraction: Hiding implementation details to focus on essential functionalities.
7 Key Features of OOPs
The key features of OOPs enable developers to write modular and reusable code. These features mirror real-world objects and interactions, making programming intuitive and efficient.
- Class and Object
- Encapsulation
- Inheritance
- Polymorphism
- Abstraction
- Dynamic Binding
- Message Passing
1. Class and Object
A class is a blueprint for creating objects. It defines properties (attributes) and methods (behaviors) that objects will have. And an object is an instance of a class. It represents a specific entity with values for the properties defined in the class.
Example
using System;
class Car
{
public string Brand { get; set; } // Properties
public int Speed { get; set; }
public void DisplayDetails() // Method
{
Console.WriteLine($"Brand: {Brand}, Speed: {Speed} km/h");
}
}
class Program
{
static void Main(string[] args)
{
Car car1 = new Car(); // Creating an object
car1.Brand = "Toyota";
car1.Speed = 120;
Car car2 = new Car();
car2.Brand = "Honda";
car2.Speed = 140;
car1.DisplayDetails();
car2.DisplayDetails();
}
}
class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed
def display_details(self):
print(f"Brand: {self.brand}, Speed: {self.speed} km/h")
# Main function
if __name__ == "__main__":
car1 = Car("Toyota", 120) # Creating an object
car2 = Car("Honda", 140)
car1.display_details()
car2.display_details()
class Car {
String brand;
int speed;
void displayDetails() {
System.out.println("Brand: " + brand + ", Speed: " + speed + " km/h");
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car(); // Creating an object
car1.brand = "Toyota";
car1.speed = 120;
Car car2 = new Car();
car2.brand = "Honda";
car2.speed = 140;
car1.displayDetails();
car2.displayDetails();
}
}
#include <iostream>>
#include <string>
class Car {
public:
std::string brand;
int speed;
void displayDetails() {
std::cout << "Brand: " << brand << ", Speed: " << speed << " km/h" << std::endl;
}
};
int main() {
Car car1; // Creating an object
car1.brand = "Toyota";
car1.speed = 120;
Car car2;
car2.brand = "Honda";
car2.speed = 140;
car1.displayDetails();
car2.displayDetails();
return 0;
}
Output
Brand: Toyota, Speed: 120 km/h
Brand: Honda, Speed: 140 km/h
Explanation
- Class Car: Defines properties brand and speed and a method displayDetails().
- Objects car1 and car2: Represent two specific cars with different values for brand and speed. The output will display the details of each car.
Also Read: |
Classes and Objects in Java |
Classes and Objects in C# |
Python Classes and Objects with Examples |
2. Encapsulation
Encapsulation is the process of bundling data (attributes) and methods (functions) into a single unit (class) and restricting direct access to the data using access modifiers (e.g., private). It ensures data security and integrity.
Example
using System;
class BankAccount
{
private double balance;
// Constructor to initialize the balance
public BankAccount(double initialBalance)
{
balance = initialBalance;
}
// Getter method for balance
public double GetBalance()
{
return balance;
}
// Method to deposit money
public void Deposit(double amount)
{
if (amount > 0)
{
balance += amount;
}
else
{
Console.WriteLine("Invalid deposit amount");
}
}
// Method to withdraw money
public void Withdraw(double amount)
{
if (amount > 0 && amount <= balance)
{
balance -= amount;
}
else
{
Console.WriteLine("Invalid withdrawal amount");
}
}
}
class Program
{
static void Main(string[] args)
{
BankAccount account = new BankAccount(5000);
account.Deposit(2000);
account.Withdraw(1500);
Console.WriteLine("Current Balance: " + account.GetBalance());
}
}
class BankAccount:
def __init__(self, initial_balance):
self.__balance = initial_balance # Private variable using __
def get_balance(self):
return self.__balance # Getter to access private data
def deposit(self, amount):
if amount > 0:
self.__balance += amount
else:
print("Invalid deposit amount")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Invalid withdrawal amount")
if __name__ == "__main__":
account = BankAccount(5000) # Create an account with an initial balance
account.deposit(2000) # Deposit 2000
account.withdraw(1500) # Withdraw 1500
print("Current Balance:", account.get_balance())
class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
balance = initialBalance;
}
public double getBalance() {
return balance; // Getter to access private data
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
System.out.println("Invalid deposit amount");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
System.out.println("Invalid withdrawal amount");
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(5000);
account.deposit(2000);
account.withdraw(1500);
System.out.println("Current Balance: " + account.getBalance());
}
}
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance;
public:
// Constructor to initialize balance
BankAccount(double initialBalance) {
balance = initialBalance;
}
// Getter to access the balance
double getBalance() {
return balance;
}
// Method to deposit amount
void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
cout << "Invalid deposit amount" << endl;
}
}
// Method to withdraw amount
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
cout << "Invalid withdrawal amount" << endl;
}
}
};
int main() {
// Create a BankAccount object
BankAccount account(5000); // Initial balance of 5000
account.deposit(2000); // Deposit 2000
account.withdraw(1500); // Withdraw 1500
cout << "Current Balance: " << account.getBalance() << endl;
return 0;
}
Output
Current Balance: 5500
Explanation
- Private attribute balance: Cannot be directly accessed outside the class.
- Public methods: getBalance(), deposit(), and withdraw() provide controlled access to balance.
- Encapsulation protects sensitive information like account balance.
Also Read: Understanding Encapsulation in Java |
3. Inheritance
Inheritance allows a class (child) to inherit properties and methods from another class (parent). It promotes code reuse and hierarchical relationships.
Example
using System;
class Vehicle
{
public string Brand { get; set; }
public void Start()
{
Console.WriteLine($"{Brand} vehicle is starting.");
}
}
class Car : Vehicle
{
public int Speed { get; set; }
public void DisplaySpeed()
{
Console.WriteLine($"{Brand} car is running at {Speed} km/h.");
}
}
class Program
{
static void Main(string[] args)
{
Car car = new Car
{
Brand = "Toyota",
Speed = 120
};
car.Start(); // Inherited from Vehicle
car.DisplaySpeed();
}
}
class Vehicle:
def __init__(self, brand):
self.brand = brand
def start(self):
print(f"{self.brand} vehicle is starting.")
class Car(Vehicle):
def __init__(self, brand, speed):
super().__init__(brand)
self.speed = speed
def display_speed(self):
print(f"{self.brand} car is running at {self.speed} km/h.")
if __name__ == "__main__":
car = Car("Toyota", 120)
car.start() # Inherited from Vehicle
car.display_speed()
class Vehicle {
String brand;
void start() {
System.out.println(brand + " vehicle is starting.");
}
}
class Car extends Vehicle {
int speed;
void displaySpeed() {
System.out.println(brand + " car is running at " + speed + " km/h.");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.brand = "Toyota";
car.speed = 120;
car.start(); // Inherited from Vehicle
car.displaySpeed();
}
}
#include <iostream>
#include <string>
using namespace std;
// Base class
class Vehicle {
public:
string brand;
void start() {
cout << brand << " vehicle is starting." << endl;
}
};
// Derived class
class Car : public Vehicle {
public:
int speed;
void displaySpeed() {
cout << brand << " car is running at " << speed << " km/h." << endl;
}
};
// Main function
int main() {
Car car;
car.brand = "Toyota";
car.speed = 120;
car.start(); // Inherited from Vehicle
car.displaySpeed();
return 0;
}
Output
Toyota vehicle is starting.
Toyota car is running at 120 km/h.
Explanation
- Parent class Vehicle: Defines common attributes and behaviors.
- Child class Car: Inherits Vehicle's properties and adds new functionality (displaySpeed()).
- The output shows Car accessing both inherited and its own methods.
Learn More: |
Inheritance in C# |
Types of Inheritance in C++ with Examples |
Understanding Different Types of Inheritance |
What is Inheritance in Java: Types of Inheritance in Java |
4. Polymorphism
Polymorphism allows the same method or operator to behave differently based on the context. This can be achieved through method overloading and overriding.
Example
using System;
namespace PolymorphismDemo
{
// Method Overloading (Compile-time Polymorphism)
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
// Method Overriding (Run-time Polymorphism)
class Animal
{
public virtual void Sound() // Use virtual to allow overriding
{
Console.WriteLine("This animal makes a sound.");
}
}
class Dog : Animal
{
public override void Sound() // Use override to provide a specific implementation
{
Console.WriteLine("The dog barks.");
}
}
class Program
{
static void Main(string[] args)
{
// Method Overloading
Calculator calc = new Calculator();
Console.WriteLine("Sum (int): " + calc.Add(5, 10));
Console.WriteLine("Sum (double): " + calc.Add(5.5, 3.3));
// Method Overriding
Animal myAnimal = new Dog();
myAnimal.Sound(); // Calls Dog's Sound() method
}
}
}
# Method Overloading (Compile-time Polymorphism)
class Calculator:
# Python does not support method overloading directly.
#However, we can achieve similar functionality with default arguments or type checking.
def add(self, a, b):
return a + b
# Method Overriding (Run-time Polymorphism)
class Animal:
def sound(self):
print("This animal makes a sound.")
class Dog(Animal):
def sound(self):
print("The dog barks.")
# Main function
if __name__ == "__main__":
# Method Overloading example
calc = Calculator()
print("Sum (int):", calc.add(5, 10)) # Works for integers
print("Sum (double):", calc.add(5.5, 3.3)) # Works for floats
# Method Overriding example
my_animal = Dog()
my_animal.sound() # Calls Dog's sound() method
// Method Overloading (Compile-time Polymorphism)
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
// Method Overriding (Run-time Polymorphism)
class Animal {
void sound() {
System.out.println("This animal makes a sound.");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("The dog barks.");
}
}
public class Main {
public static void main(String[] args) {
// Method Overloading
Calculator calc = new Calculator();
System.out.println("Sum (int): " + calc.add(5, 10));
System.out.println("Sum (double): " + calc.add(5.5, 3.3));
// Method Overriding
Animal myAnimal = new Dog();
myAnimal.sound(); // Calls Dog's sound() method
}
}
#include <iostream>
using namespace std;
// Method Overloading (Compile-time Polymorphism)
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
};
// Method Overriding (Run-time Polymorphism)
class Animal {
public:
virtual void sound() { // Using virtual function for overriding
cout << "This animal makes a sound." << endl;
}
};
class Dog : public Animal {
public:
void sound() override { // Overriding the sound() method
cout << "The dog barks." << endl;
}
};
int main() {
// Method Overloading
Calculator calc;
cout << "Sum (int): " << calc.add(5, 10) << endl;
cout << "Sum (double): " << calc.add(5.5, 3.3) << endl;
// Method Overriding
Animal* myAnimal = new Dog(); // Creating an object of Dog but using Animal pointer
myAnimal->sound(); // Calls Dog's sound() method
delete myAnimal; // Clean up memory
return 0;
}
Output
Sum (int): 15
Sum (double): 8.8
The dog barks.
Explanation
- Method Overloading: The add method is defined with different parameter types.
- Method Overriding: The sound method in Dog overrides the sound method in Animal.
Also Read: |
What is Polymorphism in Java? Types of Polymorphism |
Polymorphism in C++: Types of Polymorphism |
Method Overloading And Overriding In Java (With example) |
5. Abstraction
Abstraction hides implementation details and shows only essential features. It can be achieved using abstract classes and interfaces.
Example
using System;
abstract class Shape
{
// Abstract method
public abstract void Draw();
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle.");
}
}
class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
class Program
{
static void Main(string[] args)
{
Shape shape1 = new Circle();
Shape shape2 = new Rectangle();
shape1.Draw();
shape2.Draw();
}
}
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def draw(self):
pass
class Circle(Shape):
def draw(self):
print("Drawing a circle.")
class Rectangle(Shape):
def draw(self):
print("Drawing a rectangle.")
def main():
shape1 = Circle()
shape2 = Rectangle()
shape1.draw()
shape2.draw()
if __name__ == "__main__":
main()
abstract class Shape {
abstract void draw(); // Abstract method
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle.");
}
}
class Rectangle extends Shape {
void draw() {
System.out.println("Drawing a rectangle.");
}
}
public class Main {
public static void main(String[] args) {
Shape shape1 = new Circle();
Shape shape2 = new Rectangle();
shape1.draw();
shape2.draw();
}
}
#include <iostream>
using namespace std;
// Abstract class Shape
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
virtual ~Shape() {} // Virtual destructor
};
// Circle class inheriting from Shape
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing a circle." << endl;
}
};
// Rectangle class inheriting from Shape
class Rectangle : public Shape {
public:
void draw() override {
cout << "Drawing a rectangle." << endl;
}
};
int main() {
Shape* shape1 = new Circle(); // Creating object of Circle
Shape* shape2 = new Rectangle(); // Creating object of Rectangle
shape1->draw(); // Calling draw method for Circle
shape2->draw(); // Calling draw method for Rectangle
// Clean up dynamically allocated memory
delete shape1;
delete shape2;
return 0;
}
Output
Drawing a circle.
Drawing a rectangle.
Explanation
- Abstract class Shape: Defines the abstract method draw without implementation.
- Subclasses (Circle, Rectangle): Provide specific implementations of draw.
- The output shows different behaviors for a draw() depending on the object.
Also Read: |
Interfaces and Data Abstraction in C++ |
What is Abstraction in Java with Examples & Its Uses |
6. Dynamic Binding
Dynamic binding occurs when a method call is resolved at runtime instead of compile-time. It supports polymorphism by determining the actual method to invoke based on the object.
Example
using System;
class Animal
{
public virtual void Sound()
{
Console.WriteLine("This animal makes a sound.");
}
}
class Cat : Animal
{
public override void Sound()
{
Console.WriteLine("The cat meows.");
}
}
class Program
{
static void Main(string[] args)
{
Animal animal = new Cat(); // Dynamic Binding
animal.Sound(); // Calls Cat's Sound() at runtime
}
}
class Animal:
def sound(self):
print("This animal makes a sound.")
class Cat(Animal):
def sound(self):
print("The cat meows.")
# Main block
if __name__ == "__main__":
animal = Cat() # Dynamic Binding
animal.sound() # Calls Cat's sound() at runtime
class Animal {
void sound() {
System.out.println("This animal makes a sound.");
}
}
class Cat extends Animal {
void sound() {
System.out.println("The cat meows.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Cat(); // Dynamic Binding
animal.sound(); // Calls Cat's sound() at runtime
}
}
#include <iostream>
using namespace std;
// Base class
class Animal {
public:
virtual void sound() { // Virtual function for dynamic binding
cout << "This animal makes a sound." << endl;
}
};
// Derived class
class Cat : public Animal {
public:
void sound() override { // Override the base class sound method
cout << "The cat meows." << endl;
}
};
int main() {
Animal* animal = new Cat(); // Dynamic Binding
animal->sound(); // Calls Cat's sound() at runtime
delete animal; // Free memory
return 0;
}
Output
The cat meows.
Explanation
- The sound method is called on an Animal reference pointing to a Cat object.
- At runtime, the JVM determines that Cat's sound method should be invoked.
7. Message Passing
Message passing allows objects to communicate with each other by calling each other’s methods.
Example
using System;
class Player
{
public void Move(string direction)
{
Console.WriteLine("Player is moving " + direction);
}
}
class Game
{
public void Play()
{
Player player = new Player();
player.Move("forward"); // Message passing
}
}
class MainClass
{
public static void Main(string[] args)
{
Game game = new Game();
game.Play();
}
}
class Player:
def move(self, direction):
print(f"Player is moving {direction}")
class Game:
def play(self):
player = Player()
player.move("forward") # Message passing
if __name__ == "__main__":
game = Game()
game.play()
class Player {
void move(String direction) {
System.out.println("Player is moving " + direction);
}
}
class Game {
void play() {
Player player = new Player();
player.move("forward"); // Message passing
}
}
public class Main {
public static void main(String[] args) {
Game game = new Game();
game.play();
}
}
#include <iostream>
#include <string>
using namespace std;
class Player {
public:
void move(const string& direction) {
cout << "Player is moving " << direction << endl;
}
};
class Game {
public:
void play() {
Player player;
player.move("forward"); // Message passing
}
};
int main() {
Game game;
game.play();
return 0;
}
Output
Player is moving forward
Explanation
- The Game object sends a message (method call) to the Player object to perform an action (move).
- Message passing enables interaction between objects in an OOP system.
Advantages of OOP Features
- Code Reusability: Inheritance allows existing code to be reused, reducing redundancy.
- Scalability and Maintainability: Modular design makes it easier to update and expand software.
- Improved Modularity: Encapsulation ensures that each component is self-contained and manageable.
- Real-World Problem Mapping: OOP concepts align closely with real-world entities, making problem-solving intuitive.
Applications of OOP
The features of OOP are widely used across various domains, including:
- Web Development: Frameworks like Django (Python) and Spring (Java) rely on OOP for modular web applications.
- Game Development: OOP helps model characters, actions, and environments using objects.
- Enterprise Applications: Large-scale systems like ERP and CRM solutions use OOP for scalability and maintainability.
- Mobile Development: OOP principles are fundamental in Android and iOS development.
Read More: Top 50 OOPs Interview Questions and Answers |
Conclusion
The features of OOP have revolutionized software development by providing a framework that is modular, reusable, and adaptable. They simplify complex programming tasks, making code easier to write, understand, and maintain. Mastering OOP concepts is a crucial step for any aspiring developer, as these principles lead to better software design and equip developers to tackle real-world challenges effectively.
Dear Student, To enhance your knowledge and clear your doubts, Join our Tech Trendy Free Masterclasses to upgrade your tech skills with the latest skills trends, design, and practices. If you are interested in development, Scholarhat provides you with the Full-Stack .NET Developer Certification Training Course and .NET Solution Architect Certification Training, so don't be late and enroll now.