Object-Oriented Programming (OOP) is a software development methodology that focuses on “objects” to develop applications and computer programs. It encompasses several principles that assist developers in coming up with flexible, cohesive, and manageable software components. In this blog post, I will be explaining some of the basic concepts of OOP in Java such as classes, objects, data abstraction, encapsulation, inheritance, polymorphism, dynamic binding and message passing. To describe each concept, we will employ a single case scenario throughout the text with actual Java code illustrations.

Suppose the system we are developing is the Library Management System. With this system we will be able to control books, members and loans. This scenario will help us to illustrate all the concepts of OOP in Java.

Classes: The Blueprints of OOP

A class defines how objects shall be created. It declares a structure of data and the behaviors that can be exhibited by the objects. In the case of our Library Management System, we can create the Book class which has title, author, and ISBN as properties, along with a method for displaying book details.

public class Book {
    String title;
    String author;
    String ISBN;

    void displayInfo() {
        System.out.println("Title: " + title);
        System.out.println("Author: " + author);
        System.out.println("ISBN: " + ISBN);
    }
}

Objects: The Building Blocks of OOP

An object in turn is an instance of a class. It does not have variables but real values of the cells in the worksheet. In case of our Book class, we can create an object of a book and then use the method to output the information.

public class Main {
    public static void main(String[] args) {
        Book book1 = new Book();
        book1.title = "1984";
        book1.author = "George Orwell";
        book1.ISBN = "1234567890";
        book1.displayInfo();
    }
}

Data Abstraction: Focusing on Essentials

Data abstraction makes the real world a little easier to deal with by creating classes suitable to the problem. It encompasses the act of concealing the internal structure of an object while displaying the minimum number of aspects required in an application. In case of our Member class, it is acceptable to mask the internal workings and offer a friendly way to manipulate member information.

public class Member {
    private String name;
    private String memberId;

    public Member(String name, String memberId) {
        this.name = name;
        this.memberId = memberId;
    }

    public void displayMemberInfo() {
        System.out.println("Name: " + name);
        System.out.println("Member ID: " + memberId);
    }
}

Encapsulation: Hiding Implementation Details

Encapsulation ties an object’s data and the operations that can be performed on the data and protects them from other parts of the program. Through the employment of private access modifiers accompanied by the public getter methods, it is possible to encapsulate the member information in the context of the given Member class.

public class Member {
    private String name;
    private String memberId;

    public Member(String name, String memberId) {
        this.name = name;
        this.memberId = memberId;
    }

    public String getName() {
        return name;
    }

    public String getMemberId() {
        return memberId;
    }
}

Inheritance: Creating Hierarchical Relationships

Inheritance enables one class to extend another class and as a result many properties and methods can be reused. We can create a LibraryItem class and make Book extend from it this way Book will be able to share the attributes and behaviors of the LibraryItem class.

public class LibraryItem {
    String title;

    public void displayTitle() {
        System.out.println("Title: " + title);
    }
}

public class Book extends LibraryItem {
    String author;

    public void displayAuthor() {
        System.out.println("Author: " + author);
    }
}

Polymorphism: Achieving Dynamic Flexibility

Polymorphism enables methods to perform different tasks depending on the object that they are operating on thus improving flexibility and the ability to combine. Thus, Book subclass can override the displayInfo method to include its specific implementation for author information.

public class LibraryItem {
    String title;

    public void displayInfo() {
        System.out.println("Title: " + title);
    }
}

public class Book extends LibraryItem {
    String author;

    @Override
    public void displayInfo() {
        System.out.println("Title: " + title + ", Author: " + author);
    }
}

public class Main {
    public static void main(String[] args) {
        LibraryItem item = new Book();
        item.title = "1984";
        ((Book) item).author = "George Orwell";
        item.displayInfo(); // Outputs: Title: 1984, Author: George Orwell
    }
}

Dynamic Binding: Resolving Method Calls at Runtime

Dynamic binding is the type of binding that is determined at the time of function call as to which code is to be executed. This makes OOP to be more flexible and also more extensible. For instance, if we invoke the displayInfo method on a reference to the LibraryItem that refers to a Book object, the latter’s code is executed.

public class LibraryItem {
    public void displayInfo() {
        System.out.println("Library Item");
    }
}

public class Book extends LibraryItem {
    @Override
    public void displayInfo() {
        System.out.println("Book Item");
    }
}

public class Main {
    public static void main(String[] args) {
        LibraryItem item = new Book();
        item.displayInfo(); // Outputs: Book Item
    }
}

Message Passing: Communicating Between Objects

Message passing is the process whereby objects that are involved in the application exchange data (messages) with each other. As for the relationships between the objects in our system, a Book object may inform a Member object about the book loan.


public class Book {
    String title;

    public Book(String title) {
        this.title = title;
    }

    public void notifyMember(Member member) {
        System.out.println("Notifying " + member.getName() + " about book: " + title);
    }
}

public class Member {
    private String name;
    private String memberId;

    public Member(String name, String memberId) {
        this.name = name;
        this.memberId = memberId;
    }

    public String getName() {
        return name;
    }

    public String getMemberId() {
        return memberId;
    }
}

public class Main {
    public static void main(String[] args) {
        Book book = new Book("1984");
        Member member = new Member("Alice", "M001");
        book.notifyMember(member); // Outputs: Notifying Alice about book: 1984
    }
}

This will help to understand these four basic ideas and see how they work in an example, to begin with, using the power of OOP in your Java applications. Happy coding!