Les implémentations Java utilisent généralement un processus de compilation en deux étapes. Le code source Java est compilé en bytecode par le compilateur Java. Le bytecode est exécuté par une machine virtuelle Java (JVM). Les JVM modernes utilisent une technique appelée Compilation juste-à-temps (JIT) pour compiler le bytecode en instructions natives comprises par le CPU matériel à la volée au moment de l'exécution.
Certaines implémentations de la JVM peuvent choisir d'interpréter le bytecode au lieu de le compiler en JIT en code machine et de l'exécuter directement. Bien que cela soit toujours considéré comme un "interprète", c'est très différent des interprètes qui lisent et exécutent le code source de haut niveau (c'est-à-dire que dans ce cas, le code source Java n'est pas interprété directement, c'est le bytecode, sortie du compilateur Java, qui l'est).
Il est techniquement possible de compiler Java en code natif à l'avance et d'exécuter le binaire résultant. Il est également possible d'interpréter directement le code Java.
Pour résumer, selon l'environnement d'exécution, le bytecode peut être :
- compilé à l'avance et exécuté en tant que code natif (similaire à la plupart des compilateurs C++)
- compilé juste à temps et exécuté
- interprétées
- directement exécuté par un processeur supporté (le bytecode est le jeu d'instructions natif de certains processeurs)