What is Object-oriented Programming (OOP) – A Detailed Overview

Updated on July 25, 2024

Article Outline

As we make use of different objects in our daily lives, object-oriented programming also uses virtual objects or assets in a certain way to organise and write code. This is done primarily to handle complex problems, which can resemble real-life applications or gaming. Therefore, object-oriented programming breaks down the problem statement into objects and makes them valuable assets to reuse and duplicate certain code blocks to reduce complexity.

 

In this blog, we will see a detailed view of what is object-oriented programming and also we will explain the key concepts of OOP, like classes, objects, inheritance, and polymorphism. Furthermore, advantages and limitations of OOP are also mentioned along with how it is used in real-world applications.

What is OOP (Object Oriented Programming)?

A method that uses primarily objects to represent data and methods in a certain software code. These objects are instances of classes that also behave as blueprints or hardcoded assets that would be helpful in further coding for that particular software. Suppose you want to make a game that consists of many different features like avatars, weapons, character outfits, levels, missions, etc. Then, all these features can be taken as objects whose code can be reused, abstracted, and made static. Hence, an object can be inside a class that will define a property (data) and certain behaviours (methods) associated with that object.

 

Here, the objects can interact with each other to perform certain tasks. Moreover, this interaction will be based on principles like encapsulation, inheritance, abstraction, and polymorphism. In encapsulation, the core state of the objects is hidden or encapsulated to preserve certain default properties and expose only the necessary parts. On the other hand, inheritance allows new classes to directly or indirectly use the properties of the existing ones, making different inherited subclasses. Abstraction is used to save the programmer’s efforts in understanding complex code, as it hides a code block that is already programmed and does not need any modification. Finally, polymorphism makes objects reusable and makes it easy to handle different types of objects in a unified way.

*Image
Get curriculum highlights, career paths, industry insights and accelerate your technology journey.
Download brochure

Key Concepts of OOP

As we have discussed, any object formation will require certain key concepts and a particular approach to avoid errors and complex code. These concepts include some constructor and destructor methods, classes, data abstraction, encapsulation, inheritance, polymorphism, coupling, cohesion, association, aggregation, composition, modularity, dynamic binding, and message passing. Therefore it is necessary to understand all these concepts in depth for developing proper object-oriented programming skills.

Class

You can represent the class as a blueprint, which is used to create objects in a predefined manner. Also, it must have attributes and methods to completely define any object. Here, you can see a more detailed explanation through an example.

 

  • Attributes (Fields): Variables that hold data specific to an object.
  • Methods: Functions that define the behaviours of an object.

 

Example:

 

Let’s consider a ‘Person’ class that has attributes like ‘name’ and ‘age’ and methods to display these attributes.

// Define the Person class public class Person { // Attributes (fields) String name; int age;   // Method to display person's details void display() { System.out.println("Name: " + name); System.out.println("Age: " + age); } }

Object

Objects can be created using the class blueprint as it is an instance of a class.

 

  • Instantiation: Creating an instance of a class using the new keyword.
  • Accessing Attributes and Methods: Using the dot (.) operator to access attributes and methods of the object.

 

Example:

 

Consider a ‘Dog’ class with attributes like ‘breed’ and ‘size’, and methods to bark and display these attributes.

 

  • The Dog class includes a constructor to initialise the breed and size attributes.
  • It has methods bark() and display() to simulate barking and display the dog’s details.
  • In the Main class, an object dog1 is created using the constructor, and its methods are called to simulate barking and display its details.
// Define the Dog class public class Dog { // Attributes (fields) String breed; String size; // Constructor public Dog(String breed, String size) { this.breed = breed; this.size = size; } // Method to simulate barking void bark() { System.out.println("Woof! Woof!"); } // Method to display dog's details void display() { System.out.println("Breed: " + breed); System.out.println("Size: " + size); } } // Main class to test the Dog class public class Main { public static void main(String[] args) { // Create an object of the Dog class using the constructor Dog dog1 = new Dog("Labrador", "Large"); // Call methods dog1.bark(); dog1.display(); } }

Output:

Woof! Woof! Breed: Labrador Size: Large

Constructors and methods

Constructors: Constructors can be very helpful in initialising objects during their creation. Constructors can act as a particular default start to any function, as it can have default attributes and values to be assigned. They also have the same name as the class and do not have any return type.

 

Methods: Methods are functions defined within a class to perform actions on the objects.

 

Example:

Let’s take an example of a ‘Book’ class with attributes like ‘title’ and ‘author’, and methods to display these attributes.

 

  • The Book class has a constructor to initialise the title and author attributes.
  • It also has a method display() to print these attributes.
  • In the Main class, we create an object book1 of the Book class using the constructor and call the display() method to print the book’s details.
// Define the Book class public class Book { // Attributes (fields) String title; String author;   // Constructor to initialise attributes public Book(String title, String author) { this.title = title; this.author = author; }  // Method to display book's details void display() { System.out.println("Title: " + title); System.out.println("Author: " + author); } }  // Main class to test the Book class public class Main { public static void main(String[] args) { // Create an object of the Book class using the constructor Book book1 = new Book("1984", "George Orwell");  // Call method to display book's details book1.display(); } }

Output:

Title: 1984 Author: George Orwell

Encapsulation

Sometimes wrapping data or attributes and methods into a single unit or a single code block would solve many different types of errors and security issues that might happen in programming future. Hence encapsulation is the practice of controlling access to the data and methods by using access specifiers. Encapsulation ensures the internal representation of an object that is hidden from the outside.

 

Here are the access specifiers which you can use in Java.

 

  • Private: The member is accessible only within the same class.
  • Protected: The member is accessible within the same class, subclasses, and within the same package.
  • Public: The member is accessible from any other class.
  • Default: The member is accessible only within the same package (no keyword used).

 

Example:

Let’s consider a Student class that demonstrates encapsulation using different access specifiers.

 

The Student class encapsulates the attributes ‘name’ and ‘age’ by making them private. It provides public methods to get and set these attributes.

 

Public methods ‘getName()’, ‘setName()’, ‘getAge()’, and ‘setAge()’ are provided to access and modify these attributes safely.

// Define the Student class public class Student { // Private attributes (fields) private String name; private int age;  // Public method to get the name public String getName() { return name; }   // Public method to set the name public void setName(String name) { this.name = name; }   // Public method to get the age public int getAge() { return age; }   // Public method to set the age public void setAge(int age) { if (age > 0) { this.age = age; } else { System.out.println("Age must be positive."); } } }   // Main class to test the Student class public class Main { public static void main(String[] args) { // Create an object of the Student class Student student1 = new Student();   // Set attributes using public methods student1.setName("Alice"); student1.setAge(20);   // Get attributes using public methods System.out.println("Student Name: " + student1.getName()); System.out.println("Student Age: " + student1.getAge()); } }

Output:

Student Name: Alice Student Age: 20

Inheritance

Inheritance is a fundamental concept in object-oriented programming that allows a class to inherit properties and methods from another class. This promotes code reusability and establishes a relationship between classes.

 

There are several types of inheritance:

  • Single Inheritance: A class inherits from one superclass.
  • Multilevel Inheritance: A class inherits from a superclass, and another class inherits from that subclass.
  • Hierarchical Inheritance: Multiple classes inherit from a single superclass.
  • Multiple Inheritance: A class inherits from more than one superclass (not supported directly in Java, but can be achieved using interfaces).
  • Hybrid Inheritance: A combination of two or more types of inheritance (not supported directly in Java).

 

Example: Single Inheritance

 

Single inheritance occurs when a class inherits from one superclass. Let’s consider an example where ‘Car’ inherits from ‘Vehicle’.

 

Here, the ‘Vehicle’ class defines common attributes and methods for vehicles. The ‘Car’ class extends ‘Vehicle’ and inherits its properties and methods.

// Define the Vehicle class class Vehicle { // Attributes (fields) String brand; int speed;  // Method to display vehicle details public void display() { System.out.println("Brand: " + brand); System.out.println("Speed: " + speed); } }  // Define the Car class that extends Vehicle class Car extends Vehicle { // Additional attribute specific to Car int doors;  // Method to display car details public void displayCarDetails() { display(); // Call the inherited method System.out.println("Doors: " + doors); } }  // Main class to test the Vehicle and Car classes public class Main { public static void main(String[] args) { // Create an object of the Car class Car myCar = new Car();  // Set attributes myCar.brand = "Toyota"; myCar.speed = 120; myCar.doors = 4;  // Call method to display car details myCar.displayCarDetails(); } }

Output:

Brand: Toyota Speed: 120 Doors: 4
  • Multilevel Inheritance: Multiple inheritance can involve a chain of various inheritances in the same program. For example, let us take a vehicle, then its type can be defined as Vehicle -> Car -> ElectricCar. Thus, the ‘ElectricCar’ class inherits from ‘Car’, which in turn inherits from ‘Vehicle’.
class ElectricCar extends Car { // Additional attribute specific to ElectricCar int batteryCapacity;  // Method to display electric car details public void displayElectricCarDetails() { displayCarDetails(); // Call the inherited method from Car System.out.println("Battery Capacity: " + batteryCapacity + " kWh"); } }
  • Hierarchical Inheritance: This type occurs when multiple classes inherit from the same superclass. For instance, ‘Car’ and ‘Bike’ can both inherit from ‘Vehicle’.
class Bike extends Vehicle { // Additional attribute specific to Bike boolean hasCarrier; // Method to display bike details public void displayBikeDetails() { display(); // Call the inherited method from Vehicle System.out.println("Has Carrier: " + hasCarrier); } }
  • Multiple Inheritance: Java does not support multiple inheritance directly to avoid complexity and ambiguity. However, it can be achieved using interfaces. For example, a class FlyingCar could implement both Car and Flyable interfaces.
interface Flyable { void fly(); } // Define the FlyingCar class that extends Car and implements Flyable class FlyingCar extends Car implements Flyable { // Implement the fly method public void fly() { System.out.println("The car is flying."); } }
  • Hybrid Inheritance: A combination of multiple types of inheritance, which is also not directly supported in Java due to the complexity it can introduce. This can be managed using interfaces.

Abstraction

Abstraction works behind the scenes and working on certain parts of a software program, which could be hectic if not hidden in a proper way. For example, if you have a perfect code for defining a method or object, then you must hide that code block to reduce the complexity of the whole program and focus just on what the object does rather than how it does.

 

  • Abstract Class: A class that cannot be instantiated and is designed to be subclassed. It may contain abstract methods (methods without a body) that must be implemented by subclasses.
  • Abstract Method: A method that is declared without an implementation and must be overridden in a subclass.

 

Example:

Let’s take an example of an abstract class Shape that defines the common feature of different shapes. Subclasses like Circle and Rectangle will provide specific implementations.

 

  • The Shape class is an abstract class with an abstract method draw(). It declares the method but doesn’t implement it.
  • The Circle class extends Shape and provides an implementation for the draw() method.
  • The Main class creates an object of the Circle class and uses its methods.
// Define the abstract Shape class abstract class Shape { // Abstract method to draw the shape public abstract void draw(); }  // Define the Circle class that extends Shape class Circle extends Shape { private double radius;   // Constructor to initialise the radius public Circle(double radius) { this.radius = radius; }   // Implementation of the abstract method draw @Override public void draw() { System.out.println("Drawing a circle with radius: " + radius); } }  // Main class to test the Shape and Circle classes public class Main { public static void main(String[] args) { // Create an object of the Circle class Circle myCircle = new Circle(5.0);  // Call the draw method myCircle.draw(); } }

Output:

Drawing a circle with radius: 5.0

Polymorphism

Another fundamental concept of OOP is polymorphism, where you can treat objects as instances of their parent class rather than their actual class. Hence it is essential for designing and flexibility of the code.

 

  • Compile-time Polymorphism (Method Overloading): This occurs when multiple methods in the same class have the same name but different parameters.
  • Runtime Polymorphism (Method Overriding): This occurs when a subclass provides a specific implementation of a method that is already defined in its superclass.

 

Example 1: Method Overloading

 

  • The ‘Calculator’ class demonstrates method overloading with two ‘add’ methods having different parameter lists.
  • In the Main class, we create an object of the ‘Calculator’ class and call the overloaded add methods.
// Define the Calculator class public class Calculator { // Method to add two integers public int add(int a, int b) { return a + b; }  // Overloaded method to add three integers public int add(int a, int b, int c) { return a + b + c; } }  // Main class to test the Calculator class public class Main { public static void main(String[] args) { // Create an object of the Calculator class Calculator calc = new Calculator();  // Call the overloaded add methods System.out.println("Sum of 2 and 3: " + calc.add(2, 3)); System.out.println("Sum of 2, 3, and 4: " + calc.add(2, 3, 4)); } }

Output:

Sum of 2 and 3: 5 Sum of 2, 3, and 4: 9

Example 2: Method Overriding

 

  • The ‘Animal’ class has a method ‘makeSound()’.
  • The ‘Dog’ and ‘Cat’ classes override the ‘makeSound()’ method to provide specific implementations.
  • In the Main class, we create objects of the ‘Dog’ and ‘Cat’ classes and call the overridden ‘makeSound()’ method.
// Define the Animal class class Animal { // Method to make a sound public void makeSound() { System.out.println("Some generic animal sound"); } }  // Define the Dog class that extends Animal class Dog extends Animal { // Overridden method to make a sound specific to dogs @Override public void makeSound() { System.out.println("Bark"); } }  // Define the Cat class that extends Animal class Cat extends Animal { // Overridden method to make a sound specific to cats @Override public void makeSound() { System.out.println("Meow"); } }  // Main class to test the Animal, Dog, and Cat classes public class Main { public static void main(String[] args) { // Create objects of the Dog and Cat classes Animal myDog = new Dog(); Animal myCat = new Cat();  // Call the overridden makeSound method myDog.makeSound(); myCat.makeSound(); } }

Output:

Bark Meow

Coupling

Coupling refers to the degree of direct knowledge that one class has of another. Lower coupling is generally preferred as it reduces dependencies between classes, making the code easier to maintain and less prone to bugs.

 

Example:

 

The ‘Car’ class directly depends on the ‘Engine’ class, demonstrating high coupling. The ‘Car’ class creates an instance of ‘Engine’ and calls its methods.

// Define the Engine class class Engine { public void start() { System.out.println("Engine started."); } }  // Define the Car class class Car { private Engine engine;  // Constructor public Car() { this.engine = new Engine(); // High coupling }  public void startCar() { engine.start(); System.out.println("Car started."); } }  // Main class to test Car and Engine classes public class Main { public static void main(String[] args) { Car myCar = new Car(); myCar.startCar(); } }

Output:

Engine started. Car started.

Cohesion

When we talk about the relationship between methods in a single class, we use cohesion to know how closely the responsibilities are related. If we find high cohesion, then it refers to a class with more understandable code that can be easy to maintain and in handling errors.

 

Example:

The ‘Printer’ class has high cohesion as it only contains methods related to printing, making it focused and maintainable.

// Define the Printer class class Printer { // High cohesion as it focuses only on printing tasks public void printDocument(String document) { System.out.println("Printing: " + document); }  public void printPhoto(String photo) { System.out.println("Printing photo: " + photo); } }  // Main class to test the Printer class public class Main { public static void main(String[] args) { Printer myPrinter = new Printer(); myPrinter.printDocument("MyDocument.pdf"); myPrinter.printPhoto("MyPhoto.jpg"); } }

Output:

Printing: MyDocument.pdf Printing photo: MyPhoto.jpg

Association

The association represents a relationship between two classes. It defines how objects of one class are connected to objects of another.

 

Example:

The ‘Teacher’ and ‘Student’ classes are associated with each other through their objects, demonstrating an association relationship.

// Define the Teacher class class Teacher { String name;  public Teacher(String name) { this.name = name; }  public void teach() { System.out.println(name + " is teaching."); } }  // Define the Student class class Student { String name;  public Student(String name) { this.name = name; }  public void attendClass() { System.out.println(name + " is attending class."); } }  // Define the Main class to demonstrate an association public class Main { public static void main(String[] args) { Teacher teacher = new Teacher("Mr. Smith"); Student student = new Student("John");  teacher.teach(); student.attendClass(); } }

Output:

Mr. Smith is teaching. John is attending class.

Aggregation

Another form of association is aggregation. Here one of the classes contains a reference to any other class and it would have a “has-a” relationship with a whole-part scenario.

 

Example:

The ‘Person’ class contains an ‘Address’ object, demonstrating aggregation. This shows a whole-part relationship.

// Define the Address class class Address { String city; String state;  public Address(String city, String state) { this.city = city; this.state = state; } }  // Define the Person class class Person { String name; Address address; // Aggregation (has-a relationship)  public Person(String name, Address address) { this.name = name; this.address = address; }  public void display() { System.out.println(name + " lives in " + address.city + ", " + address.state); } }  // Main class to test Person and Address classes public class Main { public static void main(String[] args) { Address address = new Address("New York", "NY"); Person person = new Person("Alice", address); person.display(); } }

Output:

Alice lives in New York, NY

Composition

Composition is a stronger form of aggregation where the contained object cannot exist independently of the container object. It represents a “part-of” relationship.

 

Example:

The ‘Car’ class creates and manages the lifecycle of the ‘Engine’ class, demonstrating composition. The ‘Engine’ object cannot exist without the ‘Car’ object.

// Define the Engine class class Engine { public void start() { System.out.println("Engine started."); } }  // Define the Car class class Car { private Engine engine; // Composition (part-of relationship)  public Car() { this.engine = new Engine(); // Engine is part of Car }  public void startCar() { engine.start(); System.out.println("Car started."); } }  // Main class to test Car and Engine classes public class Main { public static void main(String[] args) { Car myCar = new Car(); myCar.startCar(); } }

Output:

Engine started. Car started.

Modularity

To further improve code manipulation with lesser complexity, modularity comes into action. It is the design principle that helps to divide a system into distinct modules that are interchangeable and each one of them holds a specific responsibility. Overall it improves the code maintainability and the code reusability.

 

Example:

The ‘MathOperations’ and ‘StringOperations’ classes, as mentioned below, are designed as separate modules. Both of them have a specific set of responsibilities. This modularity makes the code easier to manage and extend.

// Define the MathOperations class class MathOperations { public int add(int a, int b) { return a + b; }  public int subtract(int a, int b) { return a - b; } }  // Define the StringOperations class class StringOperations { public String concatenate(String a, String b) { return a + b; }  public int getLength(String str) { return str.length(); } }  // Main class to test modularity public class Main { public static void main(String[] args) { MathOperations mathOps = new MathOperations(); StringOperations stringOps = new StringOperations();  System.out.println("Sum: " + mathOps.add(5, 3)); System.out.println("Concatenated String: " + stringOps.concatenate("Hello", " World")); } }

Output:

Sum: 8 Concatenated String: Hello World

Dynamic Binding

Dynamic Binding is the occurrence of a method being invoked and it is found to be at runtime instead of compile-time, hence it is also known as late binding. This helps us to make more flexible and extensible code as until the program is running, we do not know which exact method will be called. Dynamic binding is commonly used in conjunction with polymorphism.

 

Example:

In this example, we have an ‘Animal’ class with a method ‘makeSound()’, and two subclasses ‘Dog’ and ‘Cat’ that override this method. In the Main class, an ‘Animal’ reference is assigned to different objects (Dog and Cat) at runtime, demonstrating dynamic binding.

// Define the Animal class class Animal { // Method to make a sound public void makeSound() { System.out.println("Some generic animal sound"); } }  // Define the Dog class that extends Animal class Dog extends Animal { // Overridden method to make a sound specific to dogs @Override public void makeSound() { System.out.println("Bark"); } }  // Define the Cat class that extends Animal class Cat extends Animal { // Overridden method to make a sound specific to cats @Override public void makeSound() { System.out.println("Meow"); } }  // Main class to test dynamic binding public class Main { public static void main(String[] args) { Animal myAnimal;  // Dynamic binding in action myAnimal = new Dog(); myAnimal.makeSound(); // Outputs "Bark"  myAnimal = new Cat(); myAnimal.makeSound(); // Outputs "Meow" } }

Output:

Bark Meow

Message Passing

Internally, objects can communicate with each other by sending and receiving certain information or data attributes with the help of message passing. We adopt this concept for easy implementation of encapsulation and modularity. This concept is central to object-oriented programming, where objects interact by calling each other’s methods.

 

Example:

In the example below, we have defined a Sender class with a method ‘sendMessage()’ which takes a Receiver object and a message as parameters. In the Main class, an object of the Sender class sends a message to an object of the Receiver class, demonstrating message passing.

// Define the Sender class class Sender { // Method to send a message public void sendMessage(Receiver receiver, String message) { receiver.receiveMessage(message); } }  // Define the Receiver class class Receiver { // Method to receive a message public void receiveMessage(String message) { System.out.println("Message received: " + message); } }  // Main class to test message passing public class Main { public static void main(String[] args) { Sender sender = new Sender(); Receiver receiver = new Receiver();  // Sender sends a message to Receiver sender.sendMessage(receiver, "Hello, this is a message!"); } }

Output:

Message received: Hello, this is a message!
  • Reusability: For the reusable purpose of code blocks, the classes can be taken in different programs or projects, saving development time.
  • Modularity: Here, the Programs can be divided further into smaller manageable sections.
  • Scalability: One of the main benefits we see in OOP systems is we can expand current OOP software projects with minimal impact on existing code.
  • Encapsulation: Data and methods are put together inside a secure and non-editable code, protecting the integrity of the data. With encapsulation, anyone could not have unauthorised access and modification.
  • Polymorphism: Allows objects to be treated as instances of their parent class, simplifying code and improving readability.
  • Inheritance: New classes can inherit properties and methods from existing classes, promoting code reuse and reducing redundancy.

Limitations of OOP

Despite its many advantages, OOP also has some limitations:

  • Complexity: OOP can introduce complexity in design and implementation, requiring careful planning and design.
  • Performance: OOP can sometimes lead to slower performance compared to procedural programming due to additional layers of abstraction and memory overhead.
  • Learning Curve: OOP concepts can be challenging for beginners to grasp, requiring an understanding of various principles.
  • Overhead: Managing objects and classes can add overhead to the program, resulting in larger codebases.

Applications of OOP

OOP is widely used in many areas of software development due to its versatility and powerful features:

  • Software Development: Today, we generally use OOP for the development of a wide range of software applications, including enterprise applications, desktop software, and web applications.
  • Game Development: OOP is ideal for designing complex game systems, allowing for the creation of interactive objects and facilitating modular and reusable code.
  • Graphical User Interfaces (GUIs): OOP is used to develop user-friendly interfaces, supporting event-driven programming and improving user interaction.
  • Real-Time Systems: OOP is used in real-time systems where objects can represent real-world entities and interactions, making the system easier to design and understand.

Conclusion

In conclusion, we certainly needed a solution to develop a certain approach towards software programming that is fixed, compiled with rules and regulations, along with a structure that can be studied by every programmer. Hence, object-oriented programming has given us this opportunity to perfectly program and transform the way we approach software development. Object-oriented programming always closely resembles the concepts of real-world problems and can be greatly inspired by the solutions presented to us to reduce complexity for upcoming software developments.

 

Despite its advantages, OOP also has limitations such as complexity and performance overhead. However, the benefits often outweigh the drawbacks, making OOP a preferred choice for many developers. Understanding the key concepts like classes, objects, inheritance, and polymorphism is essential for leveraging the full potential of OOP in your projects.

FAQs
Object-oriented programming is a method of programming based on objects and classes.
The four main principles are encapsulation, inheritance, polymorphism, and abstraction.
A class is a blueprint for creating objects and defining their properties and behaviours.
An object is an instance of a class containing data and methods.
Inheritance allows a class to inherit properties and methods from another class.
Polymorphism allows objects to be treated as instances of their parent class.
Encapsulation hides the internal state of an object and exposes only necessary parts.
An abstract class cannot be instantiated and is designed to be subclassed.

Updated on July 25, 2024

Link
left dot patternright dot pattern

Programs tailored for your success

Popular

Management

Data Science

Finance

Technology

Future Tech

Upskill with expert articles

View all
Hero Vired logo
Hero Vired is a leading LearnTech company dedicated to offering cutting-edge programs in collaboration with top-tier global institutions. As part of the esteemed Hero Group, we are committed to revolutionizing the skill development landscape in India. Our programs, delivered by industry experts, are designed to empower professionals and students with the skills they need to thrive in today’s competitive job market.
Blogs
Reviews
Events
In the News
About Us
Contact us
Learning Hub
18003093939     ·     hello@herovired.com     ·    Whatsapp
Privacy policy and Terms of use

|

Sitemap

© 2024 Hero Vired. All rights reserved