Menu
Coddy logo textTech

Banking System

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 85 of 87.

This challenge tasks you with building a Banking System that models real-world financial operations using OOP principles.

Your system should handle different account types like SavingsAccount and CheckingAccount, both extending a base Account class. Each account type has unique behaviors: savings accounts might earn interest, while checking accounts could have overdraft protection.

Think about how to model transactions. An interface like Transactable could define methods for deposit() and withdraw(). Consider using custom exceptions such as InsufficientFundsException to handle invalid operations gracefully.

A Bank class might manage multiple accounts and customers, demonstrating composition. You could apply the Factory pattern to create different account types, or use the Observer pattern to notify customers of account changes.

Focus on encapsulation to protect sensitive data like balances and account numbers, and use polymorphism to process different account types uniformly.

challenge icon

Challenge

Easy

Let's build a complete Banking System that demonstrates your mastery of OOP principles! You'll create a system with different account types, transaction handling, and proper exception management—all organized across multiple files.

You'll organize your code across seven files:

  • Transactable.java: Create an interface that defines the contract for financial operations. It should declare methods deposit(double amount) and withdraw(double amount), both returning a boolean to indicate success.
  • InsufficientFundsException.java: Create a custom exception that extends Exception. The constructor should accept the attempted withdrawal amount and current balance, creating a message in the format Insufficient funds: attempted [amount], available [balance].
  • Account.java: Create an abstract base class representing any bank account. Include private fields for accountNumber (String) and balance (double), a constructor that initializes both, appropriate getters, a protected setter for balance (so subclasses can modify it), and an abstract method getAccountType() that returns a String. Implement a getDetails() method that returns "[accountNumber] ([accountType]): $[balance]" with balance formatted to 2 decimal places.
  • SavingsAccount.java: Create a class that extends Account and implements Transactable. Add a private field for interestRate (double). The constructor takes accountNumber, initial balance, and interestRate. Implement getAccountType() to return "Savings". For deposit(), add the amount to balance and return true. For withdraw(), if the amount exceeds balance, return false; otherwise subtract and return true. Add a method applyInterest() that increases the balance by the interest rate percentage.
  • CheckingAccount.java: Create a class that extends Account and implements Transactable. Add a private field for overdraftLimit (double). The constructor takes accountNumber, initial balance, and overdraftLimit. Implement getAccountType() to return "Checking". For deposit(), add the amount and return true. For withdraw(), allow withdrawal if amount is less than or equal to balance plus overdraftLimit; return false if it exceeds this limit.
  • Bank.java: Create a class that manages accounts using an ArrayList. Include methods:
    • addAccount(Account account) - adds an account to the bank
    • findAccount(String accountNumber) - returns the Account or null if not found
    • transfer(String fromAccount, String toAccount, double amount) - transfers money between accounts, throwing InsufficientFundsException if the source account has insufficient funds. On success, return "Transferred $[amount] from [fromAccount] to [toAccount]"
    • getTotalDeposits() - returns the sum of all account balances
  • Main.java: Build your banking system! You'll receive a comma-separated string of commands:
    • SAVINGS:accountNumber:balance:interestRate
    • CHECKING:accountNumber:balance:overdraftLimit
    • DEPOSIT:accountNumber:amount
    • WITHDRAW:accountNumber:amount
    • TRANSFER:fromAccount:toAccount:amount
    • INTEREST:accountNumber

    Process each command and print:

    • SAVINGS/CHECKING: Account created: [accountNumber]
    • DEPOSIT: Deposited $[amount] to [accountNumber]
    • WITHDRAW: Withdrew $[amount] from [accountNumber] on success, or Withdrawal failed: [accountNumber]
    • TRANSFER: Print the success message or Transfer failed: [exception message]
    • INTEREST: Interest applied to [accountNumber]

    After all commands, print --- Bank Summary --- followed by each account's details (using getDetails()) and finally Total deposits: $[total] formatted to 2 decimal places.

You will receive one input: the comma-separated command string.

For example, with input SAVINGS:SAV001:1000.00:0.05,CHECKING:CHK001:500.00:200.00,DEPOSIT:SAV001:250.00,WITHDRAW:CHK001:600.00,TRANSFER:SAV001:CHK001:100.00,INTEREST:SAV001, your output would be:

Account created: SAV001
Account created: CHK001
Deposited $250.00 to SAV001
Withdrew $600.00 from CHK001
Transferred $100.00 from SAV001 to CHK001
Interest applied to SAV001
--- Bank Summary ---
SAV001 (Savings): $1207.50
CHK001 (Checking): $0.00
Total deposits: $1207.50

Notice how the checking account allows the $600 withdrawal despite only having $500 balance—that's the overdraft protection in action! The savings account earns 5% interest on its balance after the transfer. This challenge combines inheritance, interfaces, polymorphism, custom exceptions, and encapsulation into a realistic banking application!

Cheat sheet

A Banking System demonstrates OOP principles through account management, transactions, and exception handling.

Interfaces for Contracts

Define common operations using interfaces:

public interface Transactable {
    boolean deposit(double amount);
    boolean withdraw(double amount);
}

Custom Exceptions

Create custom exceptions for domain-specific errors:

public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(double attempted, double available) {
        super("Insufficient funds: attempted " + attempted + ", available " + available);
    }
}

Abstract Base Classes

Use abstract classes to define common structure with abstract methods for subclass-specific behavior:

public abstract class Account {
    private String accountNumber;
    private double balance;
    
    public abstract String getAccountType();
    
    protected void setBalance(double balance) {
        this.balance = balance;
    }
}

Inheritance and Polymorphism

Subclasses extend base classes and implement interfaces:

public class SavingsAccount extends Account implements Transactable {
    private double interestRate;
    
    @Override
    public String getAccountType() {
        return "Savings";
    }
    
    public void applyInterest() {
        setBalance(getBalance() * (1 + interestRate));
    }
}

Composition

Classes can manage collections of other objects:

public class Bank {
    private ArrayList<Account> accounts;
    
    public void addAccount(Account account) {
        accounts.add(account);
    }
    
    public Account findAccount(String accountNumber) {
        // Search and return account or null
    }
}

Encapsulation

Protect sensitive data with private fields and controlled access through getters/setters. Use protected modifiers to allow subclass access while maintaining encapsulation.

Exception Handling in Transactions

Throw custom exceptions for invalid operations:

public String transfer(String from, String to, double amount) throws InsufficientFundsException {
    if (!fromAccount.withdraw(amount)) {
        throw new InsufficientFundsException(amount, fromAccount.getBalance());
    }
    toAccount.deposit(amount);
    return "Transferred $" + amount;
}

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Create a Bank instance
        
        // TODO: Split input by comma to get individual commands
        
        // TODO: Process each command:
        // - SAVINGS:accountNumber:balance:interestRate -> Create SavingsAccount, print "Account created: [accountNumber]"
        // - CHECKING:accountNumber:balance:overdraftLimit -> Create CheckingAccount, print "Account created: [accountNumber]"
        // - DEPOSIT:accountNumber:amount -> Deposit and print "Deposited $[amount] to [accountNumber]"
        // - WITHDRAW:accountNumber:amount -> Withdraw and print success or "Withdrawal failed: [accountNumber]"
        // - TRANSFER:fromAccount:toAccount:amount -> Transfer and print success or "Transfer failed: [exception message]"
        // - INTEREST:accountNumber -> Apply interest and print "Interest applied to [accountNumber]"
        
        // TODO: After all commands, print "--- Bank Summary ---"
        
        // TODO: Print each account's details using getDetails()
        
        // TODO: Print "Total deposits: $[total]" formatted to 2 decimal places
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming