Introduction
In Java, binding refers to the process of linking a method call to the actual method implementation. There are two methods to do that. One of those methods is dynamic binding.

Dynamic binding is a mechanism where the method implementation is selected based on the actual object type at runtime. This allows for polymorphism & flexibility in object-oriented programming. This feature enables polymorphism, which allows our code to handle different types of objects smoothly, which eventually increases flexibility and reusability in OOPS.
How does Dynamic Binding Work in Java?
Dynamic binding in Java occurs through inheritance & method overriding. When a subclass extends a superclass & overrides one of its methods, the overridden method in the subclass gets called at runtime based on the actual object type, not the reference type.
Let's say we have a superclass called Animal with a method makeSound(), & two subclasses Dog & Cat that override the makeSound() method. If we create an Animal reference variable but assign it a Dog object, calling makeSound() on that variable will invoke the overridden method in the Dog class. Similarly, if we assign a Cat object to the Animal reference, the makeSound() method of the Cat class will be called.
This behavior is possible because the JVM (Java Virtual Machine) determines the actual method to be called at runtime by checking the object's type. It looks for the overridden method in the object's class hierarchy, starting from the actual class of the object & moving up to its superclasses until a matching method is found.
Dynamic binding allows us to write more generic & reusable code by programming to an interface or superclass type while the specific implementation is determined dynamically based on the actual object.
For example :
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound();
animal2.makeSound();
}
}
Output
Dog barks
Cat meows
In this example, we have a superclass Animal with a method makeSound(). The Dog & Cat classes extend the Animal class & override the makeSound() method with their own implementations.
In the main() method, we create two Animal reference variables, animal1 & animal2, but assign them Dog & Cat objects respectively. When we call the makeSound() method on animal1, the overridden method in the Dog class is invoked, printing "Dog barks". Similarly, calling makeSound() on animal2 invokes the overridden method in the Cat class, printing "Cat meows".
Note: This example shows the dynamic binding, where the method that executes is determined at runtime based on the object’s actual type, even though the variable used to reference the object is of the superclass type, `Animal`.