Java byte code is not forward compatible, JVMs are backward compatible. The difference between these properties is, that any future JVM may decide to drop backward compatibility to a certain older byte code version.
Java byte code has been designed in a way that such cut is rarely needed, but there has been a deliberate limitation of backward compatibility already. Starting with Java 8, the support for different semantics of
invokespecial of Java 1.0 has been dropped. As JVM Spec §4.1 states:
ACC_SUPER flag indicates which of two alternative semantics is to be expressed by the invokespecial instruction (§invokespecial) if it appears in this class or interface. Compilers to the instruction set of the Java Virtual Machine should set the
ACC_SUPER flag. In Java SE 8 and above, the Java Virtual Machine considers the
ACC_SUPER flag to be set in every
class file, regardless of the actual value of the flag in the
class file and the version of the
ACC_SUPER flag exists for backward compatibility with code compiled by older compilers for the Java programming language. In JDK releases prior to 1.0.2, the compiler generated
access_flags in which the flag now representing
ACC_SUPER had no assigned meaning, and Oracle's Java Virtual Machine implementation ignored the flag if it was set.
This does not imply that early Java 1.0 code doesn’t work in general. Only code relying on the outdated and now unsupported semantics of
invokespecial of that early version will break.
Another change is that the instructions
ret have been removed¹, however, this change has been tied to newer class file versions, so these instructions are still supported for older class file versions, so it doesn’t break existing code. But this could be a reason for future JVMs do drop the support for these older versions.
¹ JVM spec §4.9.1:
class file version number is 51.0 or above, then neither the jsr opcode or the jsr_w opcode may appear in the
ret instruction has not been mentioned, but doesn’t work without