In a conversation with someone else, they claimed that the version of JDK being used to compile the code does not matter vs. the JVM version being used to run the compiled code.
That seems strange to me - are there never instances where the JDK version matters? Is functionality always only dependent on the JVM the code is running on, and not on how the code is being compiled?
https://stackoverflow.com/a/19554706/2687324 talks about backwards compatibility. Is Java code also forwards compatible?
No, you can't necessarily run code compiled with a new JDK on an old JRE. Compiled classes contain a version number for the class file format; if this is newer than the runtime expects, it will refuse to load the class.
Most Java compilers support an option to target an older JRE, generating an older class file format than the compiler was built for. However, you can still run into trouble if you don't also compile against an older version of the Java runtime library. Your code might use new API that isn't in the older version of Java. The compiler, with its current version of the API, won't catch this even when you specify an older target.
For the standard javac compiler in OpenJDK, these options are -target and -bootclasspath. You might also want to set the -source option to catch usage of newer language features that require support the older class files don't provide.
Related
I am currently working on java 1.7 standard version, and I am really fun of the new features:
Nio(new input out put)
non redundant code with collections
...
I want to know if there is a way to switch the class I want to execute depending on the version of (JRE)java runtime environment it will be deployed.
You first need to create some sort of launcher app in order to check the JRE version of the environment, then you can lauch the JAR compiled for those version of the JRE. Your code cannot be compiled to newer versions of the JRE and be executed in an older one. That's why you need to complie the launcher app targeting a very old JRE.
As a side note, you can obtain the JRE version in which your program is running using:
System.getProperty("java.version")
The Java class file(s) are almost always binary compatible with new versions of the language (and the few exceptions, like using enum before the keyword was added, are fairly rare). If you restrict your usage of features to the "lowest common denominator" it is possible to compile back to an arbitrary version. That being said, Java 7 is now quite long in the tooth and I've found Java 8 to be remarkably stable. Finally, if you do choose to use new language features (like lambdas) you cannot use the earlier version of the language.
You can create two compiled build for both version to use language feature differently and have them running on jre 7, but you should just upgrade code compatibility and runtime to 1.8 (Even better)
I've read that the bytecodes that the JDK generate are generic. What I say is that they only do things like memory mapping, calling, etc... So if that's true, it would mean that I can, for example, if write a program with characteristics of JDK 1.7, I don't need to worry about if it can run in a user with JRE 1.6? (It's an example, it could happen with more exagerated cases).
So, my question is: Can I write a program with new things from 1.7 and run it in 1.6?
Can I write a program with new things from 1.7 and run it in 1.6?
No. Java is not backward compatible.
Note that it is possible to create code compatible with a Java 1.6 JRE in a 1.7 JDK by using the cross-compilation options, but that code can only use the classes, attributes, methods and language features available in version 1.6.
Can I write a program with new things from 1.7 and run it in 1.6?
The short answer is yes, but it is not officially supported. You'll have to resort to some hacks, but you can get it to work if you really want to. I wouldn't recommend doing this in a major project though.
In general bytecode is not backwards compatible because each classfile contains a version field. The JVM will refuse to run classes with a higher version than it was built for (lower versions are of course ok). By default, the javac that comes with Java 7 will create classes with version 51.0, which means they can only be executed by the JVM that comes with Java 7 or higher.
However, this doesn't mean it's impossible if you really want to create Java 6 compatible code. You can tell the compiler to generate classfiles with an earlier version. All this means is that you won't be able to use library and bytecode level features introduced in the new version.
Luckily, version 51.0 did not actually introduce any new bytecode features used by javac. All of the new features in Java 7 are implemented at compile time - the bytecode isn't any different except for the version! This means that any Java 7 class can be made to work as long as it doesn't rely on standard library updates (for example Try With Resources requires library support).
Unfortunately, javac refuses to compile Java 7 to version 50.0. But you can always change the bytecode version field yourself, either manually or with a tool. Since there were no new bytecode features, it will work just fine.
P.S. Memory mapping is down by the VM. There's no bytecode instructions for it. But you have the right idea.
I've recently moved to Java 7 in one of my projects. I claim that it can run on Java 1.5 simply because there's nothing I depend on that is in Java 6 or 7. However when compiling today I noticed this:
bootstrap class path not set in conjunction with -source 1.5
Google has found little information on this warning. Does this mean that you can't compile to Java 1.5 from Java 1.7?
This Oracle blog explains the warning:
http://blogs.oracle.com/darcy/entry/bootclasspath_older_source
The reason is, that if you fail to set rt.jar for the older platform, then:
If the second step is not taken, javac will dutifully use the old
language rules combined with new libraries, which can result in class
files that do not work on the older platform since references to
non-existent methods can get included.
Does this mean that you can't compile to Java 1.5 from Java 1.7?
No it doesn't. It means that there is a right way and a wrong way to do this ... and you are doing it the wrong way.
The right way to compile for the Java 1.5 on a Java 1.7 JDK is:
Get hold of a copy of the "rt.jar" from Java 1.5 and put it on the compilation bootclasspath.
Compile with -source 1.5 and -target 1.5.
The warning message is telling you that you haven't done the first of these.
The way that you are building right now is implicitly using the 1.7 version of "rt.jar" for the Java runtime APIs. This may work! (Indeed, it should work assuming that you've made no changes to the code since it last built on 1.5.) However, there is a risk that you may accidentally introduce dependencies on classes or methods added in Java 1.6 or 1.7. That would result in runtime errors when you try to run your application on Java 1.5.
You better be setting -source and -target 1.5.
To be really sure that you aren't accidentally incorporating dependencies on newer classes, methods, or fields, use the maven-animal-sniffer plugin or something like it.
--source 1.5 will make sure the source files comply with Java 5 conventions. --target 1.5 will make sure the generated class files comply with Java 5 conventions. Neither of these will protect you from using Java 6 or 7 library methods. You must either compile against the appropriate rt.jar using --bootclasspath, or use something like the animal-sniffer-plugin (if you are using maven) which will inspect everything's type signature, and compare with published profiles.
With the animal-sniffer-plugin, you may be in for a treat, because you can bump into 3rd party libraries that use Java 6 APIs, which may cause your build process to fail given you are pursing Java 5.
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
I've compiled my source with java version 1.6 using the parameters -source 1.5 and -target 1.5, and the compiler doesnt complain at all.
Still, the application won't run with java 1.5 due to missing methods. Ofcourse I could rewrite some of my source code to be 1.5 compliant, but what I don't understand is; shouldn't the java bytecode in the bottom be "frontwards" compliant?
Aren't the methods converted into bytecode? Is it possible to compile the 1.6 libs/methods (formely String.isEmpty()) to 1.5 bytecode and pack it all into the archive?
If you mean base Java library methods, then no, those methods are not converted to byte code when you compile; they've already been compiled to byte-code by Sun (or the third-party JVM distributer) and installed on your operating system. They are referenced and used by your compiled code.
The full set of command line options you need are:
java -source 1.5 -target 1.5 -bootclasspath /usr/jdk/jdk1.5.0_17/jre/lib/rt.jar
(Change bootclasspath to however your machine is setup.)
Of course, APIs enhancements in 1.6 will not be in 1.5. 1.5 is most of its way through its End of Service Life period, so you might want to consider a 1.6 minimum anyway.
I don't believe java will recompile the native java code backwards. So, if you make a 1.6 call - you will not be able to access it in 1.5
You can change the library you are compiling against to be an older library. In packages like eclipse, each installed JDK should appear in a "Select library" window, you can choose which one you wish to compile against.
If not, you should be able to override it in your ant file or CLI compile command.
If targeting an older JVM, this really has to be done or you may use calls that will not be available.
The source parameter only makes the compiler check at a language syntax level (source=1.4 would for example complain if it encounters generics) but won't restrict you to only using APIs available in the specified Java version.
The target parameter will make the compiler output class files that can be used by a runtime of the specified version but won't (just like -source) validate any API conformity.