Skip to Content
Course content

217: Building a Simple Banking Application

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When we start building a "real" application, like a banking system, we move away from simple logic puzzles and start dealing with state. The hardest part isn't usually the syntax; it's making sure the state of your objects changes exactly how you think it does. I'll show you a snippet of a BankAccount class that looks correct at a glance, but it's fundamentally broken.

public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void withdraw(double amount) {
        double balance = this.balance; 
        balance -= amount;
        System.out.println("Withdrawal successful!");
    }

    public double getBalance() {
        return balance;
    }
}

If you run this, you'll notice something frustrating: you can call withdraw(50.0) a hundred times, and the balance will never actually go down. Why? Let's look at what's happening under the hood.

The Local Variable Shadowing Trap

Look closely at the first line inside the withdraw method. You've written double balance = this.balance;. By adding that double keyword, you aren't updating the class's balance; you're creating a brand new local variable that just happens to have the same name as your field.

You're essentially taking a photo of the balance, scribbling a lower number on the photo, and then throwing the photo away. The actual account balance remains untouched. I've seen this happen to senior devs when they're rushing; it's a classic "shadowing" error. To fix this, we need to operate directly on the instance field.

Preventing Accidental Bankruptcy

Even after we fix the variable issue, this code has a massive logic flaw: it allows the balance to go negative. In a real banking app, we need "guards"—conditional checks that stop a method from executing if the requirements aren't met.

Here is the professional way to handle this. I've replaced the local variable with a direct update and added a check to ensure the user actually has the money they're trying to take.

public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void deposit(double amount) {
        if (amount <= 0) {
            System.out.println("Deposit amount must be positive.");
            return;
        }
        this.balance += amount;
    }

    public void withdraw(double amount) {
        if (amount <= 0) {
            System.out.println("Withdrawal amount must be positive.");
            return;
        }
        
        if (amount > this.balance) {
            System.out.println("Insufficient funds! Transaction cancelled.");
            return;
        }

        this.balance -= amount;
        System.out.println("Withdrawal successful. New balance: " + this.balance);
    }

    public double getBalance() {
        return balance;
    }
}

A quick side note: in a real production environment, you'd never use double for money because of floating-point precision errors (where 0.1 + 0.2 equals 0.30000000000000004). You'd use BigDecimal or store the balance as cents in a long. But for this lesson, we'll stick to double to keep the focus on the application logic.

Connecting the Pieces in a Main Application

Now that our BankAccount logic is sound, you can build a coordinator class to manage these accounts. The key here is to treat the BankAccount as a "black box"—your main application shouldn't touch the balance directly; it should only interact with it through the deposit and withdraw methods we just built. This is the core of encapsulation.




📋 Practical Task

Implementing an Inter-Account Transfer System

Now it's your turn to expand this system. Your task is to create a class called BankManager that can handle transfers between two different account objects.

  • Create a method public void transfer(BankAccount fromAccount, BankAccount toAccount, double amount).
  • The method must ensure that the fromAccount has enough funds before attempting the transfer.
  • If the funds are available, the amount should be withdrawn from the fromAccount and deposited into the toAccount.
  • If the funds are insufficient, print an error message and do not modify either account.
  • In your main method, create two BankAccount objects with different initial balances and perform a successful transfer and a failed transfer (due to insufficient funds) to verify your logic.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.