Java es un lenguaje semi-interpetrado por lo que se puede permitir algunas libertades como esa.
PD: Un lenguaje interpetrado es aquel que se compila y luego se interpetra el binario. El interpetre que hace esto se le llama maquina virtual.
No es técnicamente así... tu cuando compilas una aplicación JAVA o .NET, se genera lo que se conoce como código intermedio... este código debe ser compilado después por la máquina virtual para generar código ejecutable... pero ese código ejecutable sólo se genera la primera vez que se ejecuta esa parte del código.
La característica que hace que se pueda modificar partes del código mientras éste está en ejecución es que los ejecutables de .NET y JAVA no son un monolito en memoria, sino que están divididos en módulos ejecutables que se pueden descargar y recompilar sin interferir en el resto del programa.
Me explico:
Las llamadas a las funciones se configuran con punteros. Cuando la función llamada no está compilada, el puntero apunta a una instrucción de la máquina virtual que se encargará de compilar esa parte y modificar el puntero... si se requiere recompilar esa función "en caliente", basta con modificar de nuevo el puntero para que vuelva a apuntar a la máquina virtual en vez de a la porción de código que se debe recompilar.