β˜• Java

OOP Fundamentals

Learn Object-Oriented Programming through a real Bank User class β€” fields, methods, constructors, and more.

πŸ›οΈ
Class vs Instance (Object)

A class is a blueprint β€” it defines what data and behavior a type has. An instance (object) is a real thing created from that blueprint. Think of a class as a cookie cutter and instances as the cookies.

πŸ—οΈ Class = Blueprint

Defined once. Describes the shape, fields, and methods. Lives in a .java file. Does not hold actual data β€” it's just a template.

πŸ“¦ Instance = Real Object

Created with new. Each instance has its own copy of the fields with real values. You can create as many as you want.

Β«classΒ» BankUser
Fields (data)
– name : String
– accountId : String
– balance : double
Methods (behavior)
+ deposit(amount)
+ withdraw(amount)
+ transfer(to, amount)
+ getBalance() : double
new BankUser(...) creates instances ↓
instance: alice
name = "Alice"
accountId = "ACC001"
balance = 5000.0
instance: bob
name = "Bob"
accountId = "ACC002"
balance = 1200.0
  Main.java β€” creating instances
// One class, two completely independent objects
BankUser alice = new BankUser("Alice", "ACC001", 5000.0);
BankUser bob   = new BankUser("Bob",   "ACC002", 1200.0);

// They share the class blueprint, but NOT data
System.out.println(alice.getBalance()); // 5000.0
System.out.println(bob.getBalance());   // 1200.0
πŸ’‘
Key insight Changing alice's balance has zero effect on bob. Each object is an independent entity in memory β€” they just share the same class template.
πŸ“‹
Fields (Instance Variables)

Fields store the state of an object. Each instance has its own copy. They're usually declared private to protect data β€” this is called encapsulation.

  BankUser.java β€” field declarations
public class BankUser {

    // ── FIELDS ─────────────────────────────────────────
    // private = only accessible inside this class
    private String  name;        // account holder name
    private String  accountId;   // unique ID like "ACC001"
    private double  balance;     // current balance in $
    private boolean isActive;    // account open/closed flag

    // static field = shared by ALL instances (class-level)
    private static int totalAccounts = 0;

}

πŸ“Œ Instance Field

Declared without static. Each object gets its OWN copy. alice.balance and bob.balance are different memory locations.

🌐 Static Field

Declared with static. ONE copy shared by ALL instances. Perfect for counters like totalAccounts that track the whole system.

πŸ”’
Why private? If balance were public, any code could do alice.balance = -99999 β€” bypassing all validation. Making it private forces everyone to use your controlled setter methods.
  Getter / Setter example
// Getter β€” read-only access to private field
public double getBalance() {
    return balance;
}

// Getter for name
public String getName() {
    return name;
}

// Static getter β€” accessed on CLASS, not instance
public static int getTotalAccounts() {
    return totalAccounts;
}
πŸ”§
Constructor β€” Building a New User

A constructor is a special method called when you use new. It has the same name as the class and no return type. Its job: set up the initial state of a new object.

  BankUser.java β€” constructor
public class BankUser {

    private String  name;
    private String  accountId;
    private double  balance;
    private boolean isActive;
    private static int totalAccounts = 0;

    // ── CONSTRUCTOR ────────────────────────────────────
    // Same name as class, no return type
    public BankUser(String name, String accountId, double initialBalance) {

        // 'this' refers to the current instance being created
        this.name      = name;
        this.accountId = accountId;

        // Validate: balance can't start negative
        if (initialBalance < 0) {
            throw new IllegalArgumentException("Balance can't be negative");
        }
        this.balance  = initialBalance;
        this.isActive = true;        // new accounts start active

        totalAccounts++;              // update class-level counter
        System.out.println("βœ… Account created for: " + name);
    }

    // Overloaded constructor β€” start with $0 balance
    public BankUser(String name, String accountId) {
        this(name, accountId, 0.0); // delegates to main constructor
    }
}
βœ…
this keyword β€” refers to the current instance. Used to distinguish between the constructor parameter name and the field this.name. Without it, the assignment would do nothing useful.
πŸ”
Constructor overloading β€” Java lets you define multiple constructors with different parameters. this(...) calls another constructor in the same class, avoiding code duplication.
  Creating new users
// Full constructor
BankUser alice = new BankUser("Alice", "ACC001", 5000.0);
// prints: βœ… Account created for: Alice

// Overloaded constructor (zero balance)
BankUser charlie = new BankUser("Charlie", "ACC003");
// prints: βœ… Account created for: Charlie

System.out.println(BankUser.getTotalAccounts()); // 2
βš™οΈ
Methods β€” Behavior of the Object

Methods define what an object can do. They can read/modify fields, take parameters, and return values. Access modifier (public/private) + return type + name + parameters.

Method Access Returns Purpose
deposit(double amount) public void Adds money to balance
withdraw(double amount) public boolean Removes money; returns success
transfer(BankUser, double) public boolean Move money to another user
getBalance() public double Read-only balance access
printStatement() public void Print account summary
validateAmount(double) private boolean Internal validation helper
  deposit() and withdraw() methods
// ── DEPOSIT ────────────────────────────────────────
// void = returns nothing. Just changes state.
public void deposit(double amount) {
    if (!validateAmount(amount)) {
        System.out.println("❌ Invalid deposit amount");
        return;
    }
    balance += amount;   // modifies THIS instance's field
    System.out.printf("βœ… Deposited $%.2f β†’ Balance: $%.2f%n", amount, balance);
}

// ── WITHDRAW ───────────────────────────────────────
// boolean return: true = success, false = failed
public boolean withdraw(double amount) {
    if (!validateAmount(amount)) {
        System.out.println("❌ Invalid amount");
        return false;
    }
    if (amount > balance) {
        System.out.println("❌ Insufficient funds");
        return false;
    }
    balance -= amount;
    System.out.printf("βœ… Withdrew $%.2f β†’ Balance: $%.2f%n", amount, balance);
    return true;
}

// ── PRIVATE HELPER ─────────────────────────────────
// private: only usable inside BankUser class
private boolean validateAmount(double amount) {
    return amount > 0 && isActive;
}
πŸ’Έ
Transfer Method β€” Objects Interacting

The transfer() method is the most interesting β€” it takes another BankUser object as a parameter. This shows how objects can interact with each other in Java.

  transfer() method
// ── TRANSFER ───────────────────────────────────────
// Takes ANOTHER BankUser object as a parameter
public boolean transfer(BankUser recipient, double amount) {

    // Step 1: try to withdraw from THIS user
    boolean success = this.withdraw(amount);

    if (!success) {
        System.out.println("❌ Transfer failed: check your balance");
        return false;
    }

    // Step 2: deposit into recipient object
    recipient.deposit(amount);

    System.out.printf(
        "πŸ”„ Transferred $%.2f from %s β†’ %s%n",
        amount, this.name, recipient.getName()
    );
    return true;
}
πŸ”
Object as parameter β€” recipient is a reference to another BankUser object. Inside the method, calling recipient.deposit() modifies THAT object's balance. this = the sender. recipient = the receiver.
  Using transfer in Main.java
BankUser alice = new BankUser("Alice", "ACC001", 5000.0);
BankUser bob   = new BankUser("Bob",   "ACC002", 1200.0);

System.out.println("Before:");
alice.printStatement();  // Alice: $5000.00
bob.printStatement();    // Bob:   $1200.00

// Alice sends $800 to Bob
alice.transfer(bob, 800.0);

System.out.println("After:");
alice.printStatement();  // Alice: $4200.00
bob.printStatement();    // Bob:   $2000.00

// Try over-limit transfer
alice.transfer(bob, 99999.0);
// ❌ Transfer failed: check your balance
  printStatement() method
public void printStatement() {
    System.out.println("─────────────────────────");
    System.out.println("Account: "  + accountId);
    System.out.println("Holder:  "  + name);
    System.out.printf( "Balance: $%.2f%n", balance);
    System.out.println("Status:  "  + (isActive ? "Active" : "Closed"));
    System.out.println("─────────────────────────");
}
πŸ“„
Complete Source Code

The full BankUser.java and Main.java β€” everything assembled, ready to copy and run.

  BankUser.java
public class BankUser {

    // ── FIELDS ─────────────────────────────────────────
    private String  name;
    private String  accountId;
    private double  balance;
    private boolean isActive;
    private static int totalAccounts = 0; // class-level

    // ── CONSTRUCTORS ────────────────────────────────────
    public BankUser(String name, String accountId, double initialBalance) {
        if (initialBalance < 0) throw new IllegalArgumentException("Negative balance");
        this.name          = name;
        this.accountId     = accountId;
        this.balance       = initialBalance;
        this.isActive      = true;
        totalAccounts++;
        System.out.println("βœ… Account created for: " + name);
    }

    public BankUser(String name, String accountId) {
        this(name, accountId, 0.0); // delegate
    }

    // ── METHODS ─────────────────────────────────────────
    public void deposit(double amount) {
        if (!validateAmount(amount)) { System.out.println("❌ Invalid deposit"); return; }
        balance += amount;
        System.out.printf("βœ… Deposited $%.2f | New balance: $%.2f%n", amount, balance);
    }

    public boolean withdraw(double amount) {
        if (!validateAmount(amount))  { System.out.println("❌ Invalid amount");      return false; }
        if (amount > balance)           { System.out.println("❌ Insufficient funds");  return false; }
        balance -= amount;
        System.out.printf("βœ… Withdrew $%.2f | New balance: $%.2f%n", amount, balance);
        return true;
    }

    public boolean transfer(BankUser recipient, double amount) {
        if (!this.withdraw(amount)) {
            System.out.println("❌ Transfer failed"); return false;
        }
        recipient.deposit(amount);
        System.out.printf("πŸ”„ Transferred $%.2f: %s β†’ %s%n", amount, name, recipient.getName());
        return true;
    }

    public void printStatement() {
        System.out.println("─────────────────────────");
        System.out.println("Account: " + accountId);
        System.out.println("Holder:  " + name);
        System.out.printf( "Balance: $%.2f%n", balance);
        System.out.println("Status:  " + (isActive ? "Active" : "Closed"));
        System.out.println("─────────────────────────");
    }

    // ── GETTERS ─────────────────────────────────────────
    public String  getName()           { return name; }
    public String  getAccountId()      { return accountId; }
    public double  getBalance()        { return balance; }
    public boolean isActive()          { return isActive; }
    public static int getTotalAccounts() { return totalAccounts; }

    // ── PRIVATE HELPER ──────────────────────────────────
    private boolean validateAmount(double amount) {
        return amount > 0 && isActive;
    }
}
  Main.java β€” run it all
public class Main {
    public static void main(String[] args) {

        // Create two users
        BankUser alice = new BankUser("Alice", "ACC001", 5000.0);
        BankUser bob   = new BankUser("Bob",   "ACC002", 1200.0);

        // Check class-level counter
        System.out.println("Total accounts: " + BankUser.getTotalAccounts()); // 2

        // Deposit & withdraw
        alice.deposit(500.0);
        bob.withdraw(200.0);

        // Transfer
        alice.transfer(bob, 800.0);

        // Print statements
        alice.printStatement();
        bob.printStatement();

        // Try a bad transfer
        alice.transfer(bob, 999999.0); // ❌ Insufficient funds
    }
}
5
Fields defined
2
Constructors
6
Public methods
1
Private helper
4
OOP principles
πŸŽ“
4 OOP Principles applied here
Encapsulation β€” private fields + public getters.   Abstraction β€” validateAmount() hides complexity.   This example is ready for Inheritance (SavingsAccount extends BankUser)  and  Polymorphism (override methods in subclasses).