This is mostly true, but it is worth expanding as there are a lot of misconceptions about this. Java bytecode contains the JVM version it was compiled against, and later JVMs will run it just fine, even if it will no longer compile. I remember when Java added the enum keyword, and all our applications ran fine on the new JVM, even though it wouldn't compile without changes (one of our programmers used enum as the standard variable name for Enumerators). Overall, I've been very impressed with Java's cross platform and version compatibility, but I have found three causes of compatibility problems that are worth knowing about.
First, you cannot rely on non-standard libraries such as the com.sun packages. These aren't guaranteed to exist from one version of Java to the next. They aren't even guaranteed to work the same between minor Java versions. If you limit yourself to the standard libraries, you are safe. Be advised that deprecated methods may be removed in the future, though none have been removed so far.
Second, some programmers explicitly version check the JVM. This is usually for compliance issues, as some organizations feel that code should never run against a JVM that it hasn't been formally verified against. I have also seen this done because code was written to a known bug and some idiot thought it would be better to ensure that the underlying JVM always had the bug rather than work with both bugged and unbugged JVMs. Whatever your reasons, I consider this to be a bad practice, as you won't get security fixes from updated JVMs.
Third, while bytecode should always be forward compatible (barring bugs in the Java library), if you use native code you should be aware that JNI libraries are not guaranteed to work across major JVM versions. JNI libraries must be recompiled to work on newer JVMs.
There may be other sources of compatibility problems, but these are the only that I've run across them in my 15 years maintaining several million lines of Java code.