skip to content

Static vs Dynamic Binding

Which calls are bound at compile time (static, private, final methods and all field accesses) versus dispatched late through the vtable. Interviewers ask it to test whether you can explain why an inherited field and an overridden method behave differently.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

4

What is the difference between static (early) binding and dynamic (late) binding in Java, and when does each happen?

level: juniorimportance: must knowfreq 70%

answer

  1. Static = compile time + declared type; Dynamic = run time + actual object type
  2. Methods dispatch on runtime type; fields and statics on compile-time type
  3. Overload picked at compile time; override picked at run time
  4. invokevirtual/invokeinterface = dynamic; invokestatic/invokespecial = static
  5. final/private/static methods are statically bound

basics

~20 s

Binding is how a call is matched to the actual method or variable. Static binding is decided at compile time (overloaded methods, static methods, fields). Dynamic binding is decided at run time based on the real object type, which is how overridden instance methods work.

solid answer

~40 s

Binding is the process of connecting a method or field reference in code to the concrete member that actually runs or is read. Java does it in two ways. Static (early) binding is resolved by the compiler using the declared (compile-time) type of the reference; it applies to fields, static methods, private methods, final methods, and overload selection. Dynamic (late) binding is resolved at run time using the actual class of the object the reference points to; it applies to ordinary (virtual) overridden instance methods via the invokevirtual/invokeinterface mechanism. So `Animal a = new Dog(); a.sound();` runs Dog's override because the method is dynamically bound, but a field access `a.legs` reads Animal's field because fields are statically bound. This split is the engine behind runtime polymorphism.

go deeper

for a junior

Knows static binding is at compile time and dynamic binding is at run time, and that overridden instance methods use the actual object's type.

for a middle

Can list what is statically bound (fields, static/private/final methods, overload choice) vs dynamically bound (virtual instance methods) and explain the field-vs-method gotcha.

for a senior

Explains the compile-time vs runtime type distinction precisely, names invokevirtual/invokeinterface vs invokestatic/invokespecial, and connects dynamic binding to runtime polymorphism.

for a principal

Discusses vtable/method-table dispatch, JIT devirtualization (monomorphic/bimorphic inlining), and the performance and design rationale for the static/dynamic split.

## What "binding" means When you write `obj.doThing()` or read `obj.value`, the compiler and JVM must figure out *which* concrete method body runs or *which* field is read. Connecting that name in your source code to a concrete member is called **binding**. Java does this in two different moments. ## Two key types in play Every reference variable has two types: - **Compile-time type (declared/static type):** the type written in the variable's declaration. In `Animal a = new Dog();` the compile-time type of `a` is `Animal`. - **Runtime type (dynamic/actual type):** the class of the object actually created on the heap — here `Dog`. Which type is used to resolve the member is exactly what distinguishes the two bindings. ## Static (early) binding **Static binding** (also called *early binding*) happens at **compile time**, using the **compile-time type** of the reference. The decision is baked into the bytecode and never changes at run time. It applies to: - **Fields** — `a.legs` reads the field declared in the compile-time type. Fields are *never* polymorphic. - **`static` methods** — resolved by the declared type; redeclaring one in a subclass is *hiding*, not overriding. - **`private` methods** — not visible to subclasses, so no override is possible. - **`final` methods** — cannot be overridden, so the target is fixed. - **Overload resolution** — *which* overloaded signature (e.g. `print(int)` vs `print(long)`) is chosen is always a compile-time decision based on the static types of the arguments. A non-virtual call in JVM terms is one the JVM does not have to look up per-object; it uses `invokestatic` or `invokespecial`. ## Dynamic (late) binding **Dynamic binding** (also called *late binding* or *virtual invocation*) happens at **run time**, using the **runtime type** of the object. It applies to ordinary, overridable instance methods. The compiler only records the method's name and descriptor (its signature); the JVM, at the moment of the call, looks up the actual object's class and invokes the most-derived override. This is implemented with the `invokevirtual` bytecode (or `invokeinterface` for interface-typed references), typically via a per-class method table (vtable) the JVM walks. This is the mechanism that makes **runtime polymorphism** work: `Animal a = new Dog(); a.sound();` prints the Dog sound even though `a` is declared as `Animal`. ## The classic gotcha: methods vs fields ```java class Animal { int legs = 4; String kind() { return "animal"; } } class Dog extends Animal { int legs = 4; String kind() { return "dog"; } } Animal a = new Dog(); a.kind(); // "dog" -> method: dynamic binding, uses runtime type Dog a.legs; // Animal's legs -> field: static binding, uses compile-time type Animal ``` Methods dispatch on the runtime type; field *access* and `static` method *invocation* resolve on the compile-time type. Mixing these up is a very common interview trap. ## Why Java made this choice Virtual dispatch for instance methods is what lets subclasses specialize behavior — the core of OOP. Static binding for fields and statics keeps them fast and predictable (no per-object lookup) and avoids surprising shadowing semantics. The cost of dynamic dispatch is a small indirection, which modern JITs largely erase via inlining and monomorphic/bimorphic call-site optimization.

  • Given `Animal a = new Dog();`, why does `a.kind()` use Dog but `a.legs` use Animal?
    Instance methods are dynamically bound on the runtime type (Dog), so the override runs. Field access is statically bound on the compile-time type (Animal), so it reads Animal's field. Fields are never polymorphic.
  • Are static methods dynamically dispatched?
    No. A static method redeclared in a subclass is hidden, not overridden, and is resolved at compile time by the reference's declared type (invokestatic).

saying these in an interview costs you the question

  • Saying fields are polymorphic / that `a.legs` uses the runtime type
  • Claiming static methods are overridden and dynamically dispatched (they are hidden, statically bound)
  • Confusing overloading (compile-time/static) with overriding (run-time/dynamic)
  • Saying all method calls in Java are dynamically bound (private/final/static are not)

context

open as a page

Which kinds of members in Java are statically bound and which are dynamically bound? Walk through each case.

level: middleimportance: must knowfreq 60%

basics

~20 s

Dynamically bound (resolved at run time by the object's real type): ordinary overridable instance methods. Statically bound (resolved at compile time by the declared type): fields, static methods, private methods, and final methods. Overload selection is also done at compile time.

open as a page

Predict the output: with `Parent p = new Child();`, where Child overrides a method and shadows a same-named field, what does calling the method vs reading the field return, and why?

level: middleimportance: should knowfreq 45%

basics

~20 s

The method call returns Child's version because instance methods are dynamically bound to the object's real type (Child). The field read returns Parent's value because fields are statically bound to the reference's declared type (Parent). Methods follow the object; fields follow the reference.

open as a page

How does the JVM implement dynamic dispatch (virtual vs non-virtual invocation), and how does the JIT optimize it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Virtual method calls (invokevirtual/invokeinterface) look up the right override at run time, usually through a per-class method table. Non-virtual calls (invokestatic/invokespecial) have a fixed target. The JIT often removes the lookup by inlining when a call site sees only one or two types.

open as a page