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
Accountand 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:
- Create the abstract
Accountbase class with static counter and abstract withdraw() - Implement all three account types (SimpleAccount, SavingsAccount, FeeAccount)
- Create custom exception classes
- Implement Client class with static counter and account management
- 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
statickeyword for accountCount and clientCount (use$in Mermaid) - Static variables are shared across all instances of the class
- Use
abstractkeyword for Account class and withdraw() method - All subclasses MUST implement abstract withdraw() method
- Use
@Overrideannotation 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