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 anywhereprotected
: Accessible within package and subclassesdefault
(package-private): Accessible within the same packageprivate
: 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
Single Responsibility Principle (SRP)
- A class should have only one reason to change
- Each class should have a single responsibility
Open/Closed Principle (OCP)
- Classes should be open for extension, closed for modification
- Use inheritance and polymorphism to extend functionality
Liskov Substitution Principle (LSP)
- Objects of a superclass should be replaceable with objects of a subclass
- Subclasses should be substitutable for their base classes
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
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
- God Classes: Classes that do too much
- Excessive Inheritance: Deep inheritance hierarchies that are hard to maintain
- Breaking Encapsulation: Exposing internal state unnecessarily
- Misusing Inheritance: Using inheritance for code reuse instead of "is-a" relationships
- Ignoring Interface Segregation: Creating overly broad interfaces
Next Steps
Explore the specific OOP topics:
- Classes & Objects - Fundamentals of class definition and object creation
- Constructors - Object initialization and constructor overloading
- Inheritance - Extending classes and method overriding
- Polymorphism - Method overloading and dynamic dispatch
- Encapsulation - Data hiding and access control
- Abstraction - Abstract classes and interfaces
- Interfaces - Contract-based programming
- Access Modifiers - Controlling access to class members
- Static & Final - Class-level members and constants
Understanding these OOP concepts is crucial for writing maintainable, scalable Java applications and following industry best practices.