Object-Oriented Programming with Java

Jonayed BaperiJonayed Baperi
10 min read

Target Audience

This blog post is designed to cater to two primary groups of individuals:

1. Intermediate Developers in Java

If you are already familiar with Object-Oriented Programming (OOP) principles in Java but encounter uncertainties or complexities in their implementation, this content is tailored to address your concerns. You might be seeking clarification on advanced OOP concepts like inheritance, and polymorphism, or facing challenges when applying OOP principles to real-world Java projects. This guide aims to help refine your understanding, resolve doubts, and enhance your proficiency in utilizing OOP effectively within Java.

2. Beginners Exploring OOP in Java

If you're new to programming or transitioning to Java from another language, this blog post provides an ideal starting point to grasp the fundamental concepts of Object-Oriented Programming. Whether you're aiming to comprehend classes, objects, encapsulation, or inheritance, this guide breaks down these concepts in a straightforward manner within the context of Java. It is tailored to empower beginners, offering a solid foundation to embark on your programming journey confidently.

No matter your level of expertise, whether you're seeking clarification on intricate OOP concepts or just starting your Java programming journey, this post aims to provide comprehensive insights and practical examples to support your learning.

Introduction

Object-oriented programming (OOP) is a programming paradigm that organizes software design around data, or objects, rather than functions and logic. It's a way of thinking and structuring code that promotes modularity, maintainability, and reusability.

Now you thinking about what is a programming paradigm. you can check this article Basics of Programming Paradigms

OOPs Concepts

  1. Objects: Real-world objects share two characteristics: They all have state and behavior. Dogs have state (name, color) and behavior (barking, wagging tail). Software objects are conceptually similar to real-world objects: they too consist of state and related behavior. An object stores its state in fields (variables in some programming languages) and exposes its behavior through methods (functions in some programming languages). We can simply say that any entity that has state and behavior is known as an object.

  2. Class: A class is the blueprint from which individual objects are created. In the real world, you'll often find many individual objects all of the same kind. There may be thousands of other bicycles in existence, all of the same make and model. Each bicycle was built from the same set of blueprints and therefore contains the same components.

     public class Dog {
         // State
         private String name;
         private String color;
         private boolean isHungry;
    
         // Blueprint - Constructor initializes the state
         public Dog(String name, String color) {
             this.name = name;
             this.color = color;
             this.isHungry = true; // Dogs start as hungry
         }
    
         // Behavior - Method to make the dog bark
         public void bark() {
             System.out.println(name + " says Woof!");
         }
    
         // Behavior - Method to make the dog wag its tail
         public void wagTail() {
             System.out.println(name + " wags its tail happily.");
         }
    
         // Displaying the state of the dog
         public void displayState() {
             System.out.println(name + " is a " + color + " dog.");
             System.out.println(name + " is " + (isHungry ? "hungry." : "not hungry."));
         }
    
         public static void main(String[] args) {
             // Creating an individual dog object based on the blueprint
             Dog myDog = new Dog("Buddy", "Brown");
    
             // Accessing behavior and state of the dog object
             myDog.displayState();
             myDog.bark();
             myDog.wagTail();
             myDog.displayState();
         }
     }
    

    This code defines a Dog class that encapsulates the state (name, color, isHungry) and behavior (bark, wagTail, displayState) of a dog. The main method creates an instance of the Dog class and demonstrates its behavior and state.

  3. Inheritance: Inheritance is a powerful mechanism that allows you to create new classes based on existing classes. This new class inherits all the attributes and methods of the parent class, and can also add its own unique attributes and methods.

    Inheritance represents the IS-A relationship which is also known as a parent-child relationship.

    Based on class, there can be three types of inheritance in Java:

    single, multilevel and hierarchical.

    In Java programming, multiple and hybrid inheritance is supported through the interface only.

     // Superclass - Vehicle
     class Vehicle {
         private String brand;
         private String model;
    
         public Vehicle(String brand, String model) {
             this.brand = brand;
             this.model = model;
         }
    
         public void displayInfo() {
             System.out.println("Brand: " + brand);
             System.out.println("Model: " + model);
         }
     }
    
     // Subclass - Car (inherits from Vehicle)
     class Car extends Vehicle {
         private int numberOfDoors;
    
         public Car(String brand, String model, int doors) {
             super(brand, model); // Call to superclass constructor
             this.numberOfDoors = doors;
         }
    
         public void displayCarInfo() {
             super.displayInfo(); // Calling superclass method
             System.out.println("Number of Doors: " + numberOfDoors);
         }
     }
    
     // Usage
     public class VehicleDemo {
         public static void main(String[] args) {
             // Creating an instance of Car (subclass)
             Car myCar = new Car("Toyota", "Corolla", 4);
    
             // Accessing and displaying information using subclass method
             myCar.displayCarInfo();
         }
     }
    

    Bicycle serves as the superclass with common attributes and methods like currentSpeed, currentGear, speedUp, applyBrakes, changeGear, and printStatus. Each subclass (MountainBike, RoadBike, TandemBike) extends Bicycle to inherit these functionalities and can also include their specific attributes and methods, such as shockLevel, tireWidth, and numberOfSeats. The main method in BikeDemo creates instances of each bike type and demonstrates their unique behaviors.

  4. Aggregation: Aggregation is a special type of association between objects. It describes a scenario where one object (the whole) contains or owns another object (the part). Unlike inheritance, there is no parent-child relationship, and the objects coexist independently.

    Aggregation represents HAS-A relationship.

     // Engine class
     class Engine {
         private String type;
    
         public Engine(String type) {
             this.type = type;
         }
    
         public void start() {
             System.out.println("Engine started.");
         }
    
         public void stop() {
             System.out.println("Engine stopped.");
         }
     }
    
     // Car class using aggregation
     class Car {
         private String brand;
         private String model;
         private Engine carEngine; // Aggregation - Car has an Engine
    
         public Car(String brand, String model, Engine engine) {
             this.brand = brand;
             this.model = model;
             this.carEngine = engine;
         }
    
         public void startCar() {
             System.out.println("Starting the car's engine.");
             carEngine.start(); // Using Engine functionality through aggregation
         }
    
         public void stopCar() {
             System.out.println("Stopping the car's engine.");
             carEngine.stop(); // Using Engine functionality through aggregation
         }
     }
    
     // Usage
     public class CarDemo {
         public static void main(String[] args) {
             // Creating an Engine object
             Engine v8Engine = new Engine("V8");
    
             // Creating a Car object using the Engine object through aggregation
             Car myCar = new Car("Toyota", "Corolla", v8Engine);
    
             // Starting and stopping the car's engine
             myCar.startCar();
             myCar.stopCar();
         }
     }
    
    • Vehicle is a superclass with brand and model attributes and a method displayInfo to show the vehicle's information.

    • Car is a subclass of Vehicle that inherits the attributes and methods from Vehicle and adds its own attribute numberOfDoors. It also includes a method displayCarInfo to display car-specific information along with the inherited vehicle information. The main method in VehicleDemo creates an instance of Car and demonstrates using the displayCarInfo method to showcase the information of both the Vehicle and Car objects, emphasizing inheritance where Car gains access to the attributes and methods of its superclass Vehicle.

  5. Polymorphism: Polymorphism, a key concept in object-oriented programming, refers to the ability of a single interface (such as a method or function) to be used for different types or instances, allowing for flexibility and extensibility in code.

    There are two types of polymorphism: compile-time (or static) polymorphism and runtime (or dynamic) polymorphism.

    1. Compile-time Polymorphism (Method Overloading): This occurs when there are multiple methods in the same class with the same name but different parameters. The decision about which method to call is made by the compiler based on the method signature during compile-time.

       class Calculation {
           public int add(int a, int b) {
               return a + b;
           }
      
           public double add(double a, double b) {
               return a + b;
           }
       }
      

      Here, the add method is overloaded with different parameter types (integers and doubles), allowing different behaviors depending on the arguments passed.

    2. Runtime Polymorphism (Method Overriding): This occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. The decision about which method to call is made at runtime based on the actual object being referred to.

       class Animal {
           public void makeSound() {
               System.out.println("Some sound");
           }
       }
      
       class Dog extends Animal {
           @Override
           public void makeSound() {
               System.out.println("Bark");
           }
       }
      
       class Cat extends Animal {
           @Override
           public void makeSound() {
               System.out.println("Meow");
           }
       }
      

      Here, both Dog and Cat classes override the makeSound method inherited from the Animal superclass, providing specific sound implementations. The actual behavior of makeSound is determined by the type of object at runtime.

  6. Abstraction: Abstraction is a fundamental principle in object-oriented programming that focuses on hiding complex implementation details while showing only the essential features of an object.

    An interface is a group of related methods with empty bodies.

    There are two primary aspects to abstraction:

    1. Hiding Complexity: Abstraction allows us to focus on the necessary aspects of an object while hiding the unnecessary details. It simplifies the interaction with objects by exposing only the essential methods and properties.

    2. Creating Interfaces: Abstraction involves creating interfaces or abstract classes that define a set of methods or properties without providing the implementation details. These methods serve as a contract for the behavior expected from the implementing classes.

       // Abstract class defining an interface
       abstract class Shape {
           // Abstract method - no implementation details provided
           public abstract double area();
       }
      
       // Concrete classes implementing Shape
       class Circle extends Shape {
           private double radius;
      
           public Circle(double radius) {
               this.radius = radius;
           }
      
           @Override
           public double area() {
               return Math.PI * radius * radius;
           }
       }
      
       class Rectangle extends Shape {
           private double length;
           private double width;
      
           public Rectangle(double length, double width) {
               this.length = length;
               this.width = width;
           }
      
           @Override
           public double area() {
               return length * width;
           }
       }
      

      In this example, Shape is an abstract class defining an abstract method area() without providing an implementation. The Circle and Rectangle classes extend Shape and provide their own implementations of the area() method specific to their shapes. This abstraction allows users to work with shapes without worrying about the specific details of how the area is calculated for each shape.

  7. Encapsulation: Encapsulation in Java is a process of wrapping code and data together into a single unit, for example, a capsule that is mixed with several medicines.bject-oriented-programming-with

     // Class demonstrating encapsulation
     class Car {
         // Private attributes
         private String model;
         private int year;
    
         // Public methods to access and modify private attributes (getters and setters)
         public String getModel() {
             return model;
         }
    
         public void setModel(String model) {
             this.model = model;
         }
    
         public int getYear() {
             return year;
         }
    
         public void setYear(int year) {
             if (year > 0) {
                 this.year = year;
             } else {
                 System.out.println("Invalid year!");
             }
         }
     }
    
     // Usage
     public class EncapsulationExample {
         public static void main(String[] args) {
             // Creating an instance of Car
             Car myCar = new Car();
    
             // Accessing and modifying the attributes through methods (encapsulated)
             myCar.setModel("Toyota");
             myCar.setYear(2022);
    
             // Retrieving attribute values through methods
             System.out.println("Car Model: " + myCar.getModel());
             System.out.println("Car Year: " + myCar.getYear());
         }
     }
    

    Car class encapsulates model and year attributes, making them private.

    Public methods (getters and setters) provide controlled access to these attributes, allowing users to get and set their values while enforcing validation rules (e.g., ensuring a positive year value).

    The main method demonstrates how external code interacts with the Car object through its public methods without directly accessing its private attributes. This maintains encapsulation by hiding the internal state of the Car object.

Benefits of Object-Oriented Programming with Java

  1. Modularity and Reusability: OOP encourages breaking down code into smaller, manageable modules (classes and objects) that can be reused across different parts of an application or in other projects. This reusability saves time and effort in development.

  2. Maintainability and Scalability: By organizing code around objects and their interactions, OOP promotes code that is easier to maintain and extend. Changes to one part of the codebase are less likely to affect other parts, making maintenance and updates more straightforward.

  3. Abstraction for Conceptual Clarity: Abstraction allows developers to focus on essential details while hiding unnecessary complexities. This clarity in design and implementation aids in better understanding and collaboration among developers.

  4. Encapsulation for Security and Control: Encapsulation helps in controlling access to certain parts of code and data, providing security by preventing unauthorized access or modifications. This control ensures data integrity and reduces the chances of errors.

  5. Facilitates Problem-Solving: OOP aligns well with real-world problem-solving by modeling systems as objects with specific behaviors and attributes. This approach makes it easier to translate real-world scenarios into code.

  6. Supports Software Evolution: OOP's flexibility allows for easier adaptation to changing requirements and technological advancements. It enables developers to introduce new features or modify existing ones without disrupting the entire codebase.

Conclusion

Object-Oriented Programming (OOP) in Java is more than syntax—it's a mindset that fosters modular, reusable code. By organizing around objects, it simplifies maintenance, encourages scalability, and boosts security through encapsulation.

Mastering OOP principles empowers developers to build adaptable, elegant solutions. Embrace this paradigm, apply it in your projects, and let it guide your journey in Java programming. OOP isn't just a toolkit; it's a powerful approach to crafting resilient and efficient software. Keep coding, keep innovating, and let OOP be your compass in the dynamic world of Java development.

2
Subscribe to my newsletter

Read articles from Jonayed Baperi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jonayed Baperi
Jonayed Baperi

Hey, I'm Jonayed, a devoted developer with two years' experience crafting tech solutions. I thrive on tackling complex challenges and eagerly adopt new technologies. Driven by an insatiable thirst for learning, I relish mastering emerging tech to stay ahead in this dynamic field. I'm more than code – I'm an enthusiastic blogger. Through my articles, I share tech insights, problem-solving methods, and personal experiences. Join me as I document my continuous exploration in the vast tech universe. Let's connect, learn, and grow together!