JSTAcademy
0 XP
Dashboard
Crash Courses
Methods & OOP Fundamentals
16 min
Masters+170 XP
Crash Courses · Masters

Methods & OOP Fundamentals

Write well-structured methods, understand scope and the call stack, and create your first classes with fields, constructors, and encapsulation.
16 min read+170 XP on completionCert: Java / Kotlin Crash Course
Tap any word in the text below to start reading from there.

Methods & OOP Fundamentals

Method Anatomy

// accessModifier returnType methodName(paramType param1, ...) {
//     method body
//     return value;   // required unless return type is void
// }

public static int add(int a, int b) {
    return a + b;
}

// void methods return nothing
public static void printLine(String msg) {
    System.out.println(">>> " + msg);
}

// Multiple return points — valid but use sparingly
public static String classify(int n) {
    if (n < 0) return "negative";
    if (n == 0) return "zero";
    return "positive";
}

---

Method Overloading

Same method name, different parameter signatures. Java resolves at compile time:

public static double area(double radius) {
    return Math.PI * radius * radius;  // circle area
}

public static double area(double width, double height) {
    return width * height;  // rectangle area
}

public static double area(double a, double b, double c) {
    // Heron's formula for triangle given 3 sides
    double s = (a + b + c) / 2;
    return Math.sqrt(s * (s-a) * (s-b) * (s-c));
}

// main:
System.out.println(area(5.0));           // circle: 78.54
System.out.println(area(4.0, 6.0));      // rectangle: 24.0
System.out.println(area(3.0, 4.0, 5.0)); // triangle: 6.0

---

Your First Class: BankAccount

A class defines a blueprint. new creates an instance of that blueprint with its own copy of the fields:

public class BankAccount {
    // Fields — private: only this class can access them directly
    private String owner;
    private double balance;
    private int transactionCount;

    // Constructor — called with: BankAccount acct = new BankAccount("Jordan", 1000.0)
    public BankAccount(String owner, double initialBalance) {
        this.owner = owner;          // 'this.owner' = field; 'owner' alone = parameter
        this.balance = initialBalance;
        this.transactionCount = 0;
    }

    // No-arg constructor for an empty account
    public BankAccount(String owner) {
        this(owner, 0.0);  // calls the 2-arg constructor above
    }

    // Mutator methods (setters with business logic)
    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Deposit must be positive");
        balance += amount;
        transactionCount++;
    }

    public boolean withdraw(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Withdrawal must be positive");
        if (amount > balance) return false;  // insufficient funds
        balance -= amount;
        transactionCount++;
        return true;
    }

    // Accessor methods (getters)
    public double getBalance() { return balance; }
    public String getOwner()   { return owner; }
    public int getTransactionCount() { return transactionCount; }

    // toString: how this object looks when printed
    @Override
    public String toString() {
        return String.format("BankAccount[%s: $%.2f, %d txns]",
            owner, balance, transactionCount);
    }
}

Using BankAccount:

BankAccount acct = new BankAccount("Jordan", 500.0);
acct.deposit(250.0);
boolean success = acct.withdraw(100.0);
System.out.println(success);            // true
System.out.println(acct);              // BankAccount[Jordan: $650.00, 2 txns]
System.out.println(acct.getBalance()); // 650.0

boolean failed = acct.withdraw(10000.0);
System.out.println(failed);            // false — insufficient funds

---

Pass By Value (The Common Gotcha)

Java passes everything by value. For primitives, a copy of the value is passed. For objects, a copy of the reference is passed:

// Primitives: original unchanged
public static void doubleIt(int x) {
    x = x * 2;  // only changes the local copy
}
int n = 5;
doubleIt(n);
System.out.println(n);  // still 5 — original unchanged

// Objects: can mutate through the reference
public static void deposit100(BankAccount acct) {
    acct.deposit(100);  // mutates the object at the original address
}
BankAccount a = new BankAccount("Test", 500.0);
deposit100(a);
System.out.println(a.getBalance());  // 600.0 — the object was mutated

// But reassigning the reference inside the method has no effect outside
public static void reassign(BankAccount acct) {
    acct = new BankAccount("New", 0.0);  // only changes local reference copy
}
reassign(a);
System.out.println(a.getOwner());  // still "Test"

---

The @Override Annotation

@Override tells the compiler you intend to override a parent class method. If you misspell it, the compiler catches the error:

@Override
public String toString() {
    return "My custom string representation";
}

Every class in Java implicitly extends Object. Object has toString(), equals(), and hashCode() — you should override all three in well-designed value classes.

0%