key words: static/dynamic type, method overriding vs. static method hiding

1. Two Types Every Variable Has

Every Java variable has two types:

Type Meaning When determined
Static type The declared type (what's on the left of the =) Compile time
Dynamic type The actual type of the object created with new Run time

Example: A fakeA = new B(); — static type is A, dynamic type is B. These can differ whenever B extends A.

2. The Canonical Setup

public class A {
    public void f() { System.out.println("A.f"); }
    public static void g() { System.out.println("A.g"); }
}

public class B extends A {
    public void f() { System.out.println("B.f"); }
    public static void g() { System.out.println("B.g"); }
    public static void h() { System.out.println("B.h"); }
}

A a = new A();
A fakeA = new B();
B b = new B();

3. Non-static Methods → Overriding → Dynamic Type Wins

f() is an instance method, so B.f() overrides A.f(). At runtime, Java looks at the object's actual (dynamic) type to decide which version runs — this is dynamic dispatch, implemented via a method table attached to the object itself.

4. Static Methods → Hiding, Not Overriding → Static Type Wins

g() is static, so it belongs to the class, not to any object. B's g() does not override A's g() — it's a completely independent method that happens to share a name. This is called hiding, and it's resolved at compile time using the variable's declared (static) type only.

image.png

Mental model: fakeA.g() is really just syntactic sugar for A.g(). Java doesn't even need an object to exist to call it.

5. New Methods Not in the Static Type → Compile Error