# OOP Fundamentals

## 1. Classes and Objects

A class is a blueprint; an object (instance) is a concrete piece of memory built from that blueprint via `new`.

```java
public class Account {
    String owner;
    double balance;

    Account(String owner, double balance) {
        this.owner = owner;
        this.balance = balance;
    }

    void deposit(double amount) {
        balance += amount;
    }
}

Account a = new Account("Ada", 100.0);
a.deposit(50.0);
System.out.println(a.balance); // 150.0
```

Every field not explicitly initialized gets a type-appropriate default (`0`, `0.0`, `false`, `null`) the moment the object is allocated, **before** the constructor body runs.

## 2. Constructors and `this`

A constructor has no return type and shares the class's name. `this` refers to the current instance and is most commonly needed to disambiguate a field from a same-named parameter, or to call another constructor in the same class.

```java
public class Point {
    int x, y;

    Point(int x, int y) {
        this.x = x;  // this.x = field, x = parameter — without `this`, x = x would be a no-op
        this.y = y;
    }

    Point() {
        this(0, 0); // constructor chaining — must be the FIRST statement in the constructor
    }
}
```

If you write **no** constructor at all, the compiler supplies a public no-arg default constructor. The moment you write *any* constructor yourself, that default disappears — a common surprise when adding a parameterized constructor later breaks code that relied on `new Foo()`.

## 3. Encapsulation and Access Modifiers

| Modifier | Same class | Same package | Subclass (different package) | Everywhere |
|---|---|---|---|---|
| `private` | yes | no | no | no |
| (package-private, no modifier) | yes | yes | no | no |
| `protected` | yes | yes | yes | no |
| `public` | yes | yes | yes | yes |

```java
public class BankAccount {
    private double balance; // hidden — only accessible through methods below

    public double getBalance() {
        return balance;
    }

    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount must be positive");
        balance += amount;
    }
}
```

Encapsulation isn't just "hide the field" — it's controlling the **invariants**: `deposit` can reject invalid input, but a public `balance` field could be set to anything by any caller.

## 4. Static vs Instance Members

```java
public class Counter {
    static int totalCreated = 0;  // ONE copy, shared across all instances, tied to the class
    int id;                          // one copy PER instance

    Counter() {
        id = ++totalCreated;
    }
}

Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.totalCreated); // 2 — accessed via the class, not an instance
System.out.println(c1.id); // 1
System.out.println(c2.id); // 2
```

Static methods cannot use `this` and cannot call instance (non-static) methods or fields directly — there is no receiving object to resolve them against.

## 5. Method Overloading

Multiple methods can share a name if their parameter lists differ (in number, order, or type) — this is resolved entirely at **compile time** based on the declared (static) types of the arguments.

```java
void print(int x) { System.out.println("int: " + x); }
void print(double x) { System.out.println("double: " + x); }
void print(String x) { System.out.println("String: " + x); }

print(5);      // "int: 5"
print(5.0);    // "double: 5.0"
print("hi");   // "String: hi"
```

Overload resolution prefers, in order: an exact match, then a widening primitive conversion, then autoboxing, then varargs — which means adding an overload can silently change which method an *existing* call site resolves to.

## 6. Instance Initializer Blocks and Field Initialization Order

Fields and instance initializer blocks run, in the order they appear in the source file, **after** default zeroing and **before** the constructor body:

```java
public class Demo {
    int a = 10;
    { System.out.println("initializer block, a=" + a); } // runs 2nd
    int b = a + 5;                                          // runs 3rd
    Demo() {
        System.out.println("constructor, b=" + b);          // runs 4th
    }
}
```

## 7. Packages and Imports

Packages namespace classes and map (by convention, not requirement) to directory structure. `import` is purely a compile-time convenience for using a shorter name — it has zero runtime effect.

```java
package com.example.banking;

import java.util.List; // lets you write "List" instead of "java.util.List"
```

![An object on the heap has a header (mark word + class pointer) followed by its declared fields in memory order](diagrams/01-object-layout.svg)

## Under the Hood

**Every object on the heap begins with an object header, not just its fields.** In HotSpot, a typical object's memory layout is: a **mark word** (8 bytes on 64-bit JVMs — holds the identity hash code, GC age bits, and the object's lock state for `synchronized`), followed by a **class pointer** (a reference to the object's `Class` metadata — normally compressed to 4 bytes on modern heaps under 32GB via "compressed oops"), followed by the object's actual declared fields in an implementation-defined order (not necessarily source order — the JVM is free to reorder/pack fields to minimize padding). This means even an "empty" object (`class Empty {}`) still consumes header bytes (commonly 16 bytes on a 64-bit JVM with compressed oops) before a single field is stored — object allocation is never free, no matter how small the class looks in source.

**`new` is a two-step process at the bytecode level: `new` then `invokespecial`.** Compiling `new Account("Ada", 100.0)` produces roughly: a `new` instruction (allocates zeroed memory for the object and pushes a reference to it), a `dup` (duplicate that reference on the operand stack), pushes for the constructor arguments, then `invokespecial` (calls the constructor on that already-allocated object). This is a real, important distinction from method calls: constructors are invoked with `invokespecial` — a direct, non-virtual dispatch resolved at compile time — never through the dynamic-dispatch mechanism (`invokevirtual`) that ordinary instance methods use, which is precisely why calling an overridable method from within a constructor (see Pitfall 3) can observe a subclass in a not-yet-fully-initialized state: the object already exists and has a type, but the subclass constructor's own field initializers haven't run yet.

**Static members live in the `Class` object's storage, not per-instance.** A `static` field is allocated once, as part of the class's metadata area (conceptually "on the Class object" — the actual memory region has changed across JVM versions, from PermGen pre-Java-8 to Metaspace since), created when the class is first loaded and initialized (triggered by first active use — first instantiation, first static method call, or first static field access), not when any instance is created. This is exactly why `Counter.totalCreated` in section 4 has a single, shared value regardless of how many `Counter` objects exist — instances never own a copy of it at all.

## Common Pitfalls

1. **Exposing a mutable field's reference through a plain getter** — `public List<String> getItems() { return items; }` hands callers a live reference to your internal list; they can mutate it and bypass every invariant your class was supposed to enforce. Return a copy or an unmodifiable view when encapsulation matters.
2. **Forgetting the default constructor disappears once you write any constructor** — adding `Account(String owner, double balance)` silently breaks every `new Account()` call elsewhere in the codebase unless you also keep a no-arg constructor.
3. **Calling an overridable instance method from a constructor** — the subclass's fields aren't initialized yet when the superclass constructor runs (per the Under the Hood note above), so an override that reads subclass state there can observe defaults (`0`/`null`) instead of the values you expect.
4. **Overload resolution surprises with autoboxing and widening** — a call like `print(5)` prefers an exact `int` overload over a `double` or boxed `Integer` overload, and adding a new, more-specific overload later can silently redirect existing call sites without any compile error.
5. **Confusing package-private (no modifier) with `protected`** — package-private grants access only within the same package, regardless of inheritance; `protected` additionally grants access to subclasses in *other* packages. Mixing these up creates accidental encapsulation leaks or unexpected `AccessDenied`-style compile errors.

## Quick Reference

```java
// class + constructor + this
class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
    Point() { this(0, 0); } // constructor chaining, must be first statement
}

// access modifiers, most to least restrictive
private        // same class only
(none)         // + same package
protected      // + subclasses (any package)
public         // everywhere

// static: one per class, not per instance
class C { static int shared; int perInstance; }

// overloading resolved at compile time by parameter types
void f(int x) {}
void f(double x) {}

// field init order: defaults -> field initializers/instance blocks (source order) -> constructor body
```
