Skip to main content

Banking Account Management System

Inheritance hierarchy for different bank account types with client management

Account (Abstract Base Class)

The parent abstract class for all bank account types.

  • id: Unique account identifier (private)
  • balance: Current account balance (protected - accessible to subclasses)
  • accountCount: Static counter for total accounts created (private, shared across all instances)
  • Account(float balance): Constructor to initialize account with starting balance, auto-increments accountCount
  • deposit(float amount): Add money to the account
  • withdraw(float amount): Abstract method - must be implemented by subclasses
  • getBalance(): Returns current balance
  • Protected balance allows subclasses to access and modify the balance
  • Cannot be instantiated directly

SimpleAccount

Standard checking account with overdraft protection.

  • overdraft: Maximum overdraft limit allowed (private)
  • SimpleAccount(float balance, float overdraft): Constructor with initial balance and overdraft limit
  • Inherits from Account
  • Overrides withdraw(): Allows withdrawal up to balance + overdraft limit
  • Throws OverdraftException if withdrawal exceeds available funds + overdraft

SavingsAccount

Interest-bearing savings account.

  • interestRate: Annual interest rate as percentage (private)
  • SavingsAccount(float rate, float balance): Constructor with interest rate and initial balance
  • Inherits from Account
  • calculateInterest(): Calculates and adds interest to balance based on interestRate
  • Cannot withdraw if balance would go negative (no overdraft allowed)

FeeAccount

Account that charges fees for transactions.

  • fee: Fixed fee charged per transaction (private)
  • FeeAccount(float balance, float fee): Constructor with initial balance and fee amount
  • Inherits from Account
  • Overrides deposit(): Charges fee for each deposit transaction
  • Overrides withdraw(): Charges fee for each withdrawal transaction
  • Useful for premium accounts with additional services

Client

Bank client that can own multiple accounts.

  • id: Unique client identifier (private)
  • clientCount: Static counter for total clients created (private, shared across all instances)
  • firstName: Client's first name (private)
  • lastName: Client's last name (private)
  • accounts: List of accounts owned by this client (private List<Account>)
  • Client(String firstName, String lastName): Constructor that auto-increments clientCount
  • calculateTotalAssets(): Sum of balances across all client accounts
  • add(Account account): Associate a new account with this client
  • One client can have zero or more accounts (one-to-many relationship)

WithdrawalException

Exception thrown when withdrawal fails.

  • Thrown when attempting to withdraw more than available balance
  • Extends Exception class
  • Used by Account and its subclasses

OverdraftException

Exception thrown when overdraft limit exceeded.

  • Thrown by SimpleAccount when withdrawal exceeds balance + overdraft limit
  • Specialized exception for overdraft scenarios
  • Extends Exception class

Practice Exercise: Implement the Banking System

Objective: Implement the complete banking system with abstract base class, static counters, and exception handling.

Requirements:

  1. Create the abstract Account base class with static counter and abstract withdraw()
  2. Implement all three account types (SimpleAccount, SavingsAccount, FeeAccount)
  3. Create custom exception classes
  4. Implement Client class with static counter and account management
  5. Demonstrate polymorphism by storing different account types in Client
💡 Click to see suggested implementation
// Custom Exceptions
public class WithdrawalException extends Exception {
public WithdrawalException(String message) {
super(message);
}
}

public class OverdraftException extends Exception {
public OverdraftException(String message) {
super(message);
}
}

// Abstract base class
public abstract class Account {
private static int accountCount = 0; // Static counter
private int id;
protected float balance;

public Account(float balance) {
this.balance = balance;
this.id = ++accountCount; // Auto-increment ID
}

public void deposit(float amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount + " | New balance: $" + balance);
}
}

// Abstract method - must be implemented by subclasses
public abstract void withdraw(float amount) throws WithdrawalException, OverdraftException;

public float getBalance() { return balance; }
public int getId() { return id; }
public static int getAccountCount() { return accountCount; }
}

// SimpleAccount class
public class SimpleAccount extends Account {
private float overdraft;

public SimpleAccount(float balance, float overdraft) {
super(balance);
this.overdraft = overdraft;
}

@Override
public void withdraw(float amount) throws OverdraftException {
if (amount > balance + overdraft) {
throw new OverdraftException("Insufficient funds! Max withdrawal: $" + (balance + overdraft));
}
balance -= amount;
System.out.println("Withdrawn: $" + amount + " | New balance: $" + balance);
}

public float getOverdraft() { return overdraft; }
}

// SavingsAccount class
public class SavingsAccount extends Account {
private float interestRate;

public SavingsAccount(float rate, float balance) {
super(balance);
this.interestRate = rate;
}

@Override
public void withdraw(float amount) throws WithdrawalException {
if (amount > balance) {
throw new WithdrawalException("Insufficient funds! Balance: $" + balance);
}
balance -= amount;
System.out.println("Withdrawn: $" + amount + " | New balance: $" + balance);
}

public void calculateInterest() {
float interest = balance * (interestRate / 100);
balance += interest;
System.out.println("Interest added: $" + interest + " | New balance: $" + balance);
}

public float getInterestRate() { return interestRate; }
}

// FeeAccount class
public class FeeAccount extends Account {
private float fee;

public FeeAccount(float balance, float fee) {
super(balance);
this.fee = fee;
}

@Override
public void deposit(float amount) {
balance += amount - fee; // Charge fee on deposit
System.out.println("Deposited: $" + amount + " (Fee: $" + fee + ") | New balance: $" + balance);
}

@Override
public void withdraw(float amount) throws WithdrawalException {
float totalAmount = amount + fee;
if (totalAmount > balance) {
throw new WithdrawalException("Insufficient funds including fee! Required: $" + totalAmount);
}
balance -= totalAmount;
System.out.println("Withdrawn: $" + amount + " (Fee: $" + fee + ") | New balance: $" + balance);
}

public float getFee() { return fee; }
}

// Client class
import java.util.ArrayList;
import java.util.List;

public class Client {
private static int clientCount = 0; // Static counter
private int id;
private String firstName;
private String lastName;
private List<Account> accounts;

public Client(String firstName, String lastName) {
this.id = ++clientCount; // Auto-increment ID
this.firstName = firstName;
this.lastName = lastName;
this.accounts = new ArrayList<>();
}

public void add(Account account) {
accounts.add(account);
System.out.println("Account #" + account.getId() + " added to " + firstName + " " + lastName);
}

public float calculateTotalAssets() {
float total = 0;
for (Account account : accounts) {
total += account.getBalance();
}
return total;
}

public String getName() { return firstName + " " + lastName; }
public int getId() { return id; }
public static int getClientCount() { return clientCount; }
public List<Account> getAccounts() { return accounts; }
}

// Example usage
public class BankingDemo {
public static void main(String[] args) {
System.out.println("=== Banking System Demo ===\n");

// Create clients
Client client1 = new Client("John", "Doe");
Client client2 = new Client("Jane", "Smith");

// Create different types of accounts
SimpleAccount checking = new SimpleAccount(1000, 500); // $1000 balance, $500 overdraft
SavingsAccount savings = new SavingsAccount(3.5f, 5000); // 3.5% interest, $5000 balance
FeeAccount premium = new FeeAccount(10000, 2); // $10000 balance, $2 fee per transaction

// Add accounts to clients
client1.add(checking);
client1.add(savings);
client2.add(premium);

System.out.println("\nTotal accounts created: " + Account.getAccountCount());
System.out.println("Total clients created: " + Client.getClientCount());

// Perform transactions
try {
System.out.println("\n=== Simple Account Operations ===");
checking.deposit(200);
checking.withdraw(800);

System.out.println("\n=== Savings Account Operations ===");
savings.calculateInterest();
savings.withdraw(500);

System.out.println("\n=== Fee Account Operations ===");
premium.deposit(100); // Will charge fee
premium.withdraw(50); // Will charge fee

System.out.println("\n=== Client Total Assets ===");
System.out.println(client1.getName() + " total assets: $" + client1.calculateTotalAssets());
System.out.println(client2.getName() + " total assets: $" + client2.calculateTotalAssets());

// Test overdraft exception
System.out.println("\n=== Testing Overdraft Exception ===");
checking.withdraw(2000); // Should throw exception

} catch (OverdraftException e) {
System.err.println("Overdraft Error: " + e.getMessage());
} catch (WithdrawalException e) {
System.err.println("Withdrawal Error: " + e.getMessage());
}
}
}

Key Implementation Points:

  • Use static keyword for accountCount and clientCount (use $ in Mermaid)
  • Static variables are shared across all instances of the class
  • Use abstract keyword for Account class and withdraw() method
  • All subclasses MUST implement abstract withdraw() method
  • Use @Override annotation when overriding methods
  • Call super(balance) in subclass constructors
  • Exceptions extend Exception class and are thrown/caught appropriately
  • Use ++ prefix increment for auto-incrementing static counters