PROGRAMMING PRINCIPLES/OOP
Core Java OOP Concepts : Simplified
This article covers the OOP concepts used for object-oriented design
Object-Oriented Programming (OOP) is a programming paradigm that revolves around the concept of objects, which represent real-world entities and their interactions. Java is an object-oriented programming language that enables developers to create modular, reusable, and scalable applications. In this article, we’ll explore all seven fundamental OOP concepts in Java: Encapsulation, Inheritance, Polymorphism, Abstraction, Association, Aggregation, and Composition along with code examples to illustrate their applications.
1. Encapsulation
Encapsulation is the process of bundling data (attributes) and methods (functions) that operate on the data within a single unit, known as a class. It helps protect an object’s internal state by restricting direct access to its attributes and providing controlled access through public methods.
Example
Let’s create a simple `BankAccount` class to demonstrate encapsulation:
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber) {
this.accountNumber = accountNumber;
this.balance = 0;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false;
}
public double getBalance() {
return balance;
}
}
In this example, we encapsulate the accountNumber
and balance
attributes by making them private. We provide public methods deposit()
, withdraw()
, and getBalance()
to interact with these attributes in a controlled manner.
2. Inheritance
Inheritance is the mechanism by which one class can inherit properties (attributes) and methods from another class. This allows developers to create new classes based on existing ones, promoting code reusability and reducing redundancy.
Example
Let’s create a simple class hierarchy to demonstrate inheritance:
public class Vehicle {
private String make;
private String model;
private int year;
public Vehicle(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
public void start() {
System.out.println("Starting the vehicle...");
}
// Getters and setters
}
public class Car extends Vehicle {
private int numberOfDoors;
public Car(String make, String model, int year, int numberOfDoors) {
super(make, model, year);
this.numberOfDoors = numberOfDoors;
}
@Override
public void start() {
System.out.println("Starting the car...");
}
// Getters and setters
}
In this example, we create a Vehicle
class and a Car
class that inherits from Vehicle
. The Car
class inherits the attributes and methods from Vehicle
and can also define its own, like numberOfDoors
. We also override the start()
method in the Car
class to provide a custom implementation.
3. Polymorphism
Polymorphism is the ability of an object to take on different forms. In Java, polymorphism allows objects of different classes to be treated as objects of a common superclass. This enables developers to write more flexible and extensible code.
Example
Let’s extend our `Vehicle` class hierarchy to demonstrate polymorphism:
public class Motorcycle extends Vehicle {
private boolean hasSidecar;
public Motorcycle(String make, String model, int year, boolean hasSidecar) {
super(make, model, year);
this.hasSidecar = hasSidecar;
}
@Override
public void start() {
System.out.println("Starting the motorcycle...");
}
// Getters and setters
}
public class PolymorphismDemo {
public static void main(String[] args) {
Vehicle car = new Car("Toyota", "Camry", 2021, 4);
Vehicle motorcycle = new Motorcycle("Harley-Davidson", "Sportster", 2021, false);
startVehicle(car);
startVehicle(motorcycle);
}
public static void startVehicle(Vehicle vehicle) {
vehicle.start();
}
}
In this example, we create a Motorcycle
class that also inherits from Vehicle
. In the PolymorphismDemo
class, we create instances of Car
and Motorcycle
and pass them to the startVehicle()
method, which accepts a Vehicle
parameter. This demonstrates polymorphism, as the startVehicle()
method can work with any object that is a subclass of Vehicle
.
4. Abstraction
Abstraction is the process of hiding the complexity of a system and exposing only the essential features to the user. In Java, abstraction can be achieved using abstract classes and interfaces. Abstract classes are classes that cannot be instantiated and can contain abstract methods (methods without a body). Interfaces define a contract for implementing classes, specifying a set of methods that must be implemented.
Example
Let’s create an abstract Animal
class and an interface Swimmable
to demonstrate abstraction:
public abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public abstract void makeSound();
// Getters and setters
}
public interface Swimmable {
void swim();
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
public class Dolphin extends Animal implements Swimmable {
public Dolphin(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println("Click!");
}
@Override
public void swim() {
System.out.println("The dolphin is swimming...");
}
}
In this example, we create an abstract Animal
class with an abstract method makeSound()
. We also create a Swimmable
interface with a swim()
method. The Dog
class extends Animal
and provides an implementation for makeSound()
. The Dolphin
class extends Animal
and implements the Swimmable
interface, providing implementations for both makeSound()
and swim()
methods.
5. Association
Association is another fundamental principle of OOP that describes the relationship between classes. It represents a general “has-a” or “uses-a” relationship between objects, allowing them to communicate. Association can be unidirectional (one-way) or bidirectional (two-way), depending on whether the relationship is defined in one or both classes.
Let’s explore the association principle with an example.
Example
Consider a library system with Author
and Book
classes:
public class Author {
private String name;
public Author(String name) {
this.name = name;
}
// Getters and setters
}
public class Book {
private String title;
private Author author;
public Book(String title, Author author) {
this.title = title;
this.author = author;
}
// Getters and setters
}
In this example, a Book
has an Author
object, representing a unidirectional association relationship. The Book
class has a reference to the Author
class, indicating that a book has an author. However, the Author
class does not have any reference to the Book
class, so the relationship is one-way.
If we want to make the relationship bidirectional, we can modify the Author
class to include a list of Book
objects:
public class Author {
private String name;
private List<Book> books;
public Author(String name) {
this.name = name;
this.books = new ArrayList<>();
}
public void addBook(Book book) {
books.add(book);
}
// Getters and setters
}
Now, the Author
class has a list of Book
objects, and the relationship between the Author
and Book
classes is bidirectional. Both classes have references to each other, allowing objects of one class to communicate with objects of the other class.
6. Aggregation
Aggregation is a weak form of association that represents a “has-a” or “part-of” relationship between objects. In aggregation, the composed object (whole) can exist independently of its parts, and the whole does not manage the lifetime of the parts. If the whole is destroyed, the parts can still exist.
Example
Consider a university system with Department
and Professor
classes:
public class Professor {
private String name;
public Professor(String name) {
this.name = name;
}
// Getters and setters
}
public class Department {
private String name;
private List<Professor> professors;
public Department(String name) {
this.name = name;
this.professors = new ArrayList<>();
}
public void addProfessor(Professor professor) {
professors.add(professor);
}
// Getters and setters
}
In this example, a Department
has a list of Professor
objects, representing an aggregation relationship. The Department
and Professor
objects can exist independently of each other. If a Department
is destroyed, the Professor
objects can still exist and be associated with other departments.
7. Composition
A composition is an intense form of association that represents a “has-a” or “part-of” relationship between objects, similar to aggregation. However, in composition, the composed object (entire) is responsible for managing the lifetime of its parts. If the entire is destroyed, its parts are also destroyed.
Example
Consider a computer system with Computer
and Processor
classes:
public class Processor {
private String model;
public Processor(String model) {
this.model = model;
}
// Getters and setters
}
public class Computer {
private String name;
private Processor processor;
public Computer(String name, String processorModel) {
this.name = name;
this.processor = new Processor(processorModel);
}
// Getters and setters
In this example, a Computer
has a Processor
object, representing a composition relationship. The Computer
object is responsible for creating and managing the lifetime of its Processor
. If a Computer
is destroyed, its Processor
object is also destroyed, as it cannot exist independently.
Conclusion
OOP concepts in Java, such as Encapsulation, Inheritance, Polymorphism, and Abstraction, are fundamental principles that help developers create modular, reusable, and scalable applications.
Association, Aggregation, and Composition are three essential principles of OOP that help define relationships between classes. While the two of them represent “has-a” or “part-of” relationships, they differ in how the lifetime of the parts is managed. Aggregation allows parts to exist independently of the whole, while composition implies that the whole is responsible for managing the lifetime of its parts. The association helps to define relationships between classes and allows objects to communicate with each other. Understanding these relationships can help you design more robust and flexible software systems.
Peace!
Related reads: Programming Principles