I'm migrating multiple projects from Java 8 to Java 11 and I was wondering if Java 8 is forward compatible with Java 11.
In other words, is it possible to use artifacts compiled against Java 11 in Java 8 projects?
No, you cannot execute Java 11 bytecode on an older (any older) JVM. This has always been the case. Each new major compiler release has a new bytecode format that won't execute on older runtimes.
You CAN however execute bytecode from an older version on a newer JVM, so executing Java 8 bytecode on a Java 11 runtime is quite possible and you can thus use Java 8 (or older) compiled libraries in Java 11 projects, if the dependency requirements are met.
Do however keep in mind that Java 9+ removed a number of packages from the core libraries that were in there with Java 8, so you may need to supply new dependencies from third parties to replace those. Most notable of those (but certainly not the only ones) are XML parsers.
You can run projects that were compiled for Java 8 in Java 11 runtime.
You can run projects that were compiled for Java 11 in Java 8 runtime if you properly set the --target during compilation to target JDK 8 or less. This will of course also limit the set of features supported in the source code.
Generally, earlier versions of JDK are supported on later runtimes (i.e. Java 11 should run on Java 17), but there are some caveats because some of the features might get deprecated or changed. Always read the release notes and test before you upgrade.
Related
Does the bytecode depend on the version of Java it was created with?
If I compiled a java file in the newest JDK, would an older JVM be able to run the .class files?
That depends on three things:
The actual Java versions you are talking about. For instance, a 1.4.0 JVM can run code compiled by a 1.4.2 compiler, but a 1.3.x JVM cannot1.
The compilation flags used. There is a -target compiler flag that tells it to generate code that will run on an older (target) JVM. And the -source compiler flag tells it to only accept the older JVM's language features. (This approach won't always work, depending on the Java language features used by your code. But if the code compiles it should work.)
The library classes that the class file uses. If it uses library classes that don't exist in the older class libraries, then it won't run ... unless you can include a JAR that back-ports the classes2. You can avoid this problem by using the -bootclasspath option to compile your code against the APIs of the older version of Java.
Does the bytecode depend on the version of the java it was created with?
Yes, modulo the points above.
1 - The Java 8 JVMS states this: "Oracle's Java Virtual Machine implementation in JDK release 1.0.2 supports class file format versions 45.0 through 45.3 inclusive. JDK releases 1.1.* support class file format versions in the range 45.0 through 45.65535 inclusive. For k ≥ 2, JDK release 1.k supports class file format versions in the range 45.0 through 44+k.0 inclusive."
2 - A backport could be problematic too. For example: 1) Things which depend on native code support would most likely require you to implement that native code support. 2) You would most likely need to put any back-port JAR file onto the bootclasspath when you run the code on the older JVM.
Does the bytecode depend on the version of the java it was created with?
Normally yes. But by using the -source, -target and -bootclasspath options, a 1.7+ compiler can be used to create binaries that are compatible with Java 1.1
First and foremost all java files have a version byte in the class header. Older jvms won't load classes with newer versions, regardless of what features they have.
JVM bytecode is forward compatible between major JVM version, but not backward compatible. However, for the best information you will have to read the JVM release notes because they typically indicate how backward compatible the bytecode is.
Edit clarification since this caused discussion in the comments
JVM bytecode is forward compatible, such that bytecode from one JVM is compatible with with later JVM releases. For example, you can take bytecode from the 1.4 JVM and run it in Java 5 or Java 6 JVM (aside from any sort of regression issues as pointed out by Andrew).
JVM bytecode is not backward compatible between JVMs, such that bytecode from a JVM is not guaranteed to work in a previous release of the JVM, as would be the case if you were attempting to run code compiled for Java 6 in a 1.4.2 JVM.
Does the bytecode depend on the version of the java it was created with?
Yes.
If I compiled a java file in the newest JDK, would an older JVM be able to run the .class files?
No. But the opposite will work, most likely. You might like see this interesting thread, it talks about backporting Java.
No, unless you specify as target the old JVM.
Eg.with Java 6 you can compile and run in Java 1.4 using:
javac -target 1.4 SomeClass.java
Obviously the source code should be 1.4 compatible.
You can compile classes that are older-version JVMs compatible if you don't use features available in higher JVMs.
javac -target 1.5 MyJava.java
javac -target 1.4 MyJava.java
Does Java support running in compatibility mode? In other words if we have JDK 8 install on system, can it be configured to run my application on 7 or previous release using the same installation ?
I can give one example like IE-11 can be switch to run as IE 8, 9, or 10 based on the compatibility option.
I agree with the comment of Kayaman.
There are 2 types of incompatibilities that could occur: bytecode changes (some feature supported in 8 and not in 7 - new Java versions tend to be upward compatible - so 8 will be able to run all 7-targeted code) and library changes which is more problematic.
If you have compiled with Java 8 targeting 7+, your bytecode will be compatible with Java 7 JVM, but you have no guarantee that it will run with Java 7 libraries.
Your best bet is then to compile and run with Java 7 - and it will (most probably) run with Java 8.
Then there are other changes that may impact your application (GC performance for instance).
I'm a little confused by how Scala and Java interact. For example, I am building a Play application which is using version 2.11.6 of Scala, and I need an object to represent a date.
Ideally, I would like to use LocalDate from Java 8, but is this possible if Scala 2.11 targets Java JDK 6?
I would like to get a better understanding of how different versions of Java work with Scala and how the two are related.
To answer your specific question, if you run scalac with JDK8, then yes you can use LocalDate with Scala 2.11.
As for the more general question, there's four basic ways Scala and Java interact that are decoupled from each other version-wise (at least in theory).
At compile time, some version of java must be running scalac
At compile time, scalac reads some version of JVM bytecode
At compile time, scalac emits some version of JVM bytecode
At run time, java (via the scala script) runs the JVM bytecode scalac emitted.
So let's see how this stacks up for Scala 2.10.5's scalac, Scala 2.11.x's scalac, and the upcoming Scala 2.12.x's scalac and Java 6, 7, and 8.
Scala 2.10.5:
scalac can be run by Java 6, 7, or 8 (due to java's backwards compatibility)
scalac can read Java 6 or 7 bytecode
scalac emits Java 6 (default), or 7 (optional) bytecode for the Scala-specific parts but if it reads Java 7 bytecode (e.g. because you're using a Java 7 library), then your final product will have Java 7 bytecode in it as well which is called by Java 6 bytecode.
Depending on your behavior in your previous step you will either have pure Java 6 bytecode which can then be run by Java 6, 7, or 8's java or bytecode with Java 7-specific features which can then only be run by Java 7 or 8's java.
Scala 2.11.x:
scalac can be run by Java 6, 7, or 8
scalac can read Java 6, 7, or 8 bytecode (depending on the version x of 2.11.x more or fewer Java 8 bytecode features can be read and used)
scalac emits Java 6 (default), or Java 7 (optional) bytecode
Same story as 2.10.5 here
Scala 2.12.x:
scalac can only be run by Java 8
scalac can read Java 8 bytecode and 7 bytecode by virtue of Java's backwards compatibility (I'm not sure about the story here whether scalac makes any additional effort to be more backwards compatible than Java 8's java is, e.g. for Java 6 compatibility)
scalac emits Java 8 bytecode
Java 8's java must be used to run the resulting binary
Just to emphasize that in theory these steps are completely decoupled, Scala 2.12's scalac could hypothetically be compiled by Scala 2.11's scalac to result in a scalac which can be run by Java 6, but whose resulting output could only be run by a Java 8 java.
In the case of Scala 2.11, you can use Java 8, as long as you provide those classes to scalac. The only special thing about LocalDate is that it is bundled with the Java Platform so you implicitly get access to it if you run scalac with Java 8 and don't if you run scalac with Java 6 or 7. If you are using a third-party Java 8 library, you could provide the compiled JAR to a Scala 2.11.x scalac to use regardless of which Java version is running scalac. That bytecode though may find its way into your own emitted JAR, which can complicate things for downstream users.
This means for any non-purely Scala project, you must let your consumers know both the Scala version and Java version you used for any JARs you generate. If your project is a library, and your consumer uses a compatible Scala version but an incompatible Java version, her/his code may compile, but will not run.
Scala version and Java version are distinct: you can run the same Scala version (let say 2.10.5) on different Java versions (e.g. 1.6, 1.7, 1.8).
But if you use JSE types which are specific to a JVM version, your Scala code won't run with a previous Java version. As if you build your Scala app/lib with a provided dependency, without having the runtime actually providing it.
Moreover, if the bytecode generated, by scalac according the configured Java version, is more recent than the one of the JRE (compiled in Java 8 but executed with JRE 1.6), it cannot be interpreted, as any plain Java code.
Will a Java 8 codebase that is compiled with Java 8 work on a Java 6 VM?
Yes, but if you don't make use of features of java 7 and java 8 ...
If the codebase is written using features of newer java versions, then no there's no way to do it!
However, if you want to run them on java 6 you have to set java 6 compliance level(with -source 1.6 -target 1.6 javac parameters) when you compile source code files, to make them compatible with java 6. If you don't set the compliance as shown above, you will get an UnsupportedClassVersionError :
java.lang.UnsupportedClassVersionError:Unsupported major.minor version XX.X
It generally only works the other way around, so running a Java 6 compiled application should run on a JVM of a higher version. The other way around is not possible I'm afraid, unless you don't use any new features and tell javac that your source version is 1.6 (i.e. -source 1.6) which essentially means you're programming Java 6 :).
Depending on what Java 8 features the code is using, you may be able to run Retrolambda on it and use the results in a Java 6 JVM. Maybe.
I am compiling my web app in Netbeans against Java EE 5. I know that the String.isEmpty() function is only supported in Java 6. Having said that, I can still compile my project using the .isEmpty() in my code.
How come Netbeans is allowing my web app to compile if I am compiling against Java EE 5?
The compiler level is not equal to the JDK level you use for compiling. The compiler level only checks for the syntax and of course creates a different output. But the compiler itself will use the jdk on your classpath so if you compiled with java 5 option but with the java 6 jars on your classpath the code will compile without an error.
You should check your classpath.
As many have pointed out, Java EE versions are not strictly tied with Java SE (JDK) versions. Mostly, they require a minimal Java SE version but are compatible with later versions.
Java EE 5 specification (downloadable PDF here) says:
This specification requires that containers provide a Java Compatible™ runtime
environment, as defined by the Java 2 Platform, Standard Edition, v5.0 specification
(J2SE)
Since JSE versions are backwards compatible, you can take a container compatible with Java 5 and run it on top of Java SE 6 or Java SE 7.
You can check the compatibility level that Netbeans is using by checking the project "Properties > Source > Source/Binary Format"
If you still have doubts about "Java vs Java EE" you can look for several questions here on SO about the difference between Java SE and Java EE.