Как правило, скомпилированный код - это точный набор инструкций, которые требуется ЦПУ для «выполнения» программы. В Java скомпилированный код представляет собой точный набор инструкций для «виртуального ЦП», который должен работать одинаково на каждой физической машине.
Таким образом, в некотором смысле разработчики языка Java решили, что язык и скомпилированный код будут независимыми от платформы, но, поскольку в конечном итоге код должен работать на физической платформе, они решили поместить всю платформу зависимый код в JVM.
Это требование для JVM противоречит вашему примеру с Turbo C. В Turbo C компилятор будет генерировать код, зависящий от платформы, и JVM не будет работать аналогично, поскольку скомпилированная программа Turbo C может выполняться непосредственно процессором.
В Java процессор выполняет JVM, которая зависит от платформы. Эта работающая JVM затем выполняет байт-код Java, который не зависит от платформы, при условии, что у вас есть JVM, доступная для его выполнения. Вы можете сказать, что при написании кода Java вы не программируете код, выполняемый на физической машине, вы пишете код, который будет выполняться на виртуальной машине Java.
Единственный способ, которым весь этот байт-код Java работает на всех виртуальных машинах Java, заключается в том, что был написан довольно строгий стандарт для работы виртуальных машин Java. Это означает, что независимо от того, какую физическую платформу вы используете, та часть, где байт-код Java взаимодействует с JVM, гарантированно будет работать только одним способом. Поскольку все JVM работают абсолютно одинаково, один и тот же код везде работает одинаково без перекомпиляции. Если вы не можете пройти тесты, чтобы убедиться, что это то же самое, вы не можете называть свою виртуальную машину «виртуальной машиной Java».
Конечно, есть способы, которые могут нарушить переносимость Java-программы. Вы можете написать программу, которая ищет файлы, найденные только в одной операционной системе (например, cmd.exe). Вы можете использовать JNI, который эффективно позволяет помещать скомпилированный код C или C ++ в класс. Вы можете использовать соглашения, которые работают только для определенной операционной системы (например, предполагая, что ":" разделяет каталоги). Но вам гарантированно никогда не придется перекомпилировать вашу программу для другой машины, если вы не делаете что-то действительно особенное (например, JNI).