1. java
  2. /oop

Master Object-Oriented Programming in Java

Object-Oriented Programming in Java

Object-Oriented Programming (OOP) is a programming paradigm that revolves around the concept of "objects" that contain data (attributes) and code (methods). Java is a purely object-oriented language that implements all four fundamental principles of OOP.

What is Object-Oriented Programming?

OOP is a programming methodology that organizes software design around data, or objects, rather than functions and logic. An object can be defined as a data field that has unique attributes and behavior.

Four Pillars of OOP

1. Encapsulation

  • Definition: Bundling data and methods that operate on that data within a single unit (class)
  • Purpose: Data hiding and security
  • Implementation: Private variables with public getter/setter methods

2. Inheritance

  • Definition: Mechanism where a new class inherits properties and methods from an existing class
  • Purpose: Code reusability and establishing relationships between classes
  • Implementation: extends keyword for classes, implements for interfaces

3. Polymorphism

  • Definition: Ability of objects to take multiple forms
  • Purpose: Flexibility and dynamic method resolution
  • Implementation: Method overriding and method overloading

4. Abstraction

  • Definition: Hiding complex implementation details while showing only essential features
  • Purpose: Simplifying complex systems
  • Implementation: Abstract classes and interfaces

Key OOP Concepts in Java

Classes and Objects

// Class definition
public class Car {
    // Attributes (data)
    private String brand;
    private String model;
    private int year;
    
    // Constructor
    public Car(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
    }
    
    // Methods (behavior)
    public void start() {
        System.out.println("The " + brand + " is starting...");
    }
    
    public void stop() {
        System.out.println("The " + brand + " has stopped.");
    }
}

// Object creation
Car myCar = new Car("Toyota", "Camry", 2023);
myCar.start(); // Method call

Access Modifiers

Java provides four levels of access control:

  • public: Accessible from anywhere
  • protected: Accessible within package and subclasses
  • default (package-private): Accessible within the same package
  • private: Accessible only within the same class

Benefits of OOP in Java

Modularity

  • Code is organized into separate, interchangeable components
  • Each class has a specific responsibility

Reusability

  • Classes can be reused in different programs
  • Inheritance allows extending existing functionality

Scalability

  • Easy to add new features without affecting existing code
  • Modular design supports large applications

Maintainability

  • Changes to one class don't affect others (when properly designed)
  • Easier debugging and testing

Security

  • Encapsulation protects data from unauthorized access
  • Access modifiers control visibility

Real-World Example: Banking System

// Base class
public abstract class Account {
    protected String accountNumber;
    protected double balance;
    protected String customerName;
    
    public Account(String accountNumber, String customerName) {
        this.accountNumber = accountNumber;
        this.customerName = customerName;
        this.balance = 0.0;
    }
    
    // Abstract method - must be implemented by subclasses
    public abstract void calculateInterest();
    
    // Concrete methods
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: $" + amount);
        }
    }
    
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
            return true;
        }
        return false;
    }
    
    public double getBalance() {
        return balance;
    }
}

// Subclass demonstrating inheritance
public class SavingsAccount extends Account {
    private double interestRate;
    
    public SavingsAccount(String accountNumber, String customerName, double interestRate) {
        super(accountNumber, customerName); // Call parent constructor
        this.interestRate = interestRate;
    }
    
    @Override
    public void calculateInterest() {
        double interest = balance * interestRate / 100;
        balance += interest;
        System.out.println("Interest added: $" + interest);
    }
}

// Another subclass
public class CheckingAccount extends Account {
    private double overdraftLimit;
    
    public CheckingAccount(String accountNumber, String customerName, double overdraftLimit) {
        super(accountNumber, customerName);
        this.overdraftLimit = overdraftLimit;
    }
    
    @Override
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= (balance + overdraftLimit)) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
            return true;
        }
        return false;
    }
    
    @Override
    public void calculateInterest() {
        // Checking accounts typically don't earn interest
        System.out.println("No interest for checking accounts");
    }
}

OOP Design Principles

SOLID Principles

  1. Single Responsibility Principle (SRP)

    • A class should have only one reason to change
    • Each class should have a single responsibility
  2. Open/Closed Principle (OCP)

    • Classes should be open for extension, closed for modification
    • Use inheritance and polymorphism to extend functionality
  3. Liskov Substitution Principle (LSP)

    • Objects of a superclass should be replaceable with objects of a subclass
    • Subclasses should be substitutable for their base classes
  4. Interface Segregation Principle (ISP)

    • Clients should not be forced to depend on interfaces they don't use
    • Create specific interfaces rather than large, general-purpose ones
  5. Dependency Inversion Principle (DIP)

    • High-level modules should not depend on low-level modules
    • Both should depend on abstractions

Common OOP Design Patterns

Singleton Pattern

public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    private DatabaseConnection() {
        // Private constructor prevents instantiation
    }
    
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
}

Factory Pattern

public abstract class Animal {
    public abstract void makeSound();
}

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

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

public class AnimalFactory {
    public static Animal createAnimal(String type) {
        switch (type.toLowerCase()) {
            case "dog":
                return new Dog();
            case "cat":
                return new Cat();
            default:
                throw new IllegalArgumentException("Unknown animal type");
        }
    }
}

Best Practices

Class Design

  • Keep classes focused on a single responsibility
  • Use meaningful class and method names
  • Follow naming conventions (PascalCase for classes)

Encapsulation

  • Make fields private and provide public accessors when needed
  • Validate input in setter methods
  • Use immutable objects when possible

Inheritance

  • Use inheritance to model "is-a" relationships
  • Prefer composition over inheritance when appropriate
  • Don't create deep inheritance hierarchies

Polymorphism

  • Program to interfaces, not implementations
  • Use method overriding to provide specific implementations
  • Take advantage of dynamic method dispatch

Common Mistakes to Avoid

  1. God Classes: Classes that do too much
  2. Excessive Inheritance: Deep inheritance hierarchies that are hard to maintain
  3. Breaking Encapsulation: Exposing internal state unnecessarily
  4. Misusing Inheritance: Using inheritance for code reuse instead of "is-a" relationships
  5. Ignoring Interface Segregation: Creating overly broad interfaces

Next Steps

Explore the specific OOP topics:

Understanding these OOP concepts is crucial for writing maintainable, scalable Java applications and following industry best practices.