I think the concept you're trying to understand is using an interface as a type. In a java program, a variable can be declared to have the type of some defined interface. Classes that implement that interface can then be instantiated for variables of the interface type. However, only methods declared on the interface can be used. At compile time, the interface is used for type checking. At runtime however, the bytecode that actually does the work comes from the interface implementor. An example:
public interface foo {
public void bar();
}
public class A implements foo {
public void bar() {
// some code
}
}
public class Example {
public static void main(String[] args) {
foo aFoo = new A();
aFoo.bar();
}
}
In the class Example, a variable named aFoo is declared to be of type foo, the interface. A, which implements the foo interface, will contain the code to do the work of method bar(). In the class Example, the variable aFoo gets an instance of the class A, so whatever code is in the bar() method of A will be executed when aFoo.bar() is called even though aFoo is declared to be of type foo.
So, we've established that all the work is done in class A. The two classes and one interface above can each be defined in their own file. All three resulting .class files can be packaged into a jar and shipped to customers as version 1.0 of the program. Sometime later, a bug might be discovered in the implementation of bar() in class A. Once a fix is developed, assuming the changes are all contained inside the bar() method, just the .class file for A needs to be shipped to customers. The updated A.class can be inserted into the program's .jar file (.jar files are just .zip files after all) overwriting the previous, broken version of A.class. When the program is restarted the JVM will load the new implementation of A.bar() and the class Example will get the new behavior.
As with almost everything, more complicated programs can get, well, more complicated. But the principles are the same.