I have Java 7 installed but set my projects JDK compliance to Java 6. Unfortunately it turned out that this is not the same as compiling with Java 6.
For example, the interface javax.imageio.stream.ImageInputStream
extends Closable in Java 7 which is not the case in Java 6. Using an ImageInputStream where a Closable is expected compiles fine under my settings (Java 7 complying to Java 6) but gives an compile error when using Java 6.
Is this supposed to be this way?
In order to compile Java code for an older JRE, you need to do two things:
Set the compliance level appropriately. This, as explained by dystroy, makes sure the compiler produces bytecode that the old JVM can understand.
Use the old Java system libraries. This makes sure you compile against the version of the Java system libraries (java.lang.* , java.net.* etc.) that shipped with the old JRE.
You have covered the first point, but not the second, hence your problem.
How to do this depends on your build environment.
In Eclipse, the Java system library to use is set as part of the build path: Go to Project properties / Java Build Path / Libraries, then remove the wrong "JRE System Library" and add the right one using "Add Library...".
When compiling with plain javac, you use option -bootclasspath. Example: javac -target 1.5 -bootclasspath jdk1.5.0/lib/rt.jar OldCode.java (from the javac manpage, section "Cross-Compilation Options").
What you have set with the JDK compliance is mostly the class format.
There were several evolutions in the bytecode format of classes. A JDK can compile in a previous format while a VM can't execute a class compiled on a more recent format.
But the used libraries always are the one available in the JDK you use for execution. The JDK 6 version of javax.imageio.stream.ImageInputStream isn't bundled with Java 7.
Use the bootclasspath option when compiling. I thought the JDK 7 warned of not doing so, if doing cross-compilation.
Related
I was wondering if there is any difference running/building a software under JDK 8 and using compiler compliance level 1.7 vs JDK 7 as system default? I am more interested in reference to Android building, building apps, Eclipse, Android Studio, etc.
Yes, there are loads of new classes in the JDK 1.8, for example, the java.time classes. You won't get those if you build in JDK 1.7; but you will be able to use them if you build in JDK 1.8 with compiler compliance level 1.7.
Yes there is a difference between running/building a software under JDK 8 and using compiler compliance level 1.7 vs JDK 7 as system default.
running a software under JDK 8 and using compiler compliance level : You compile in jdk 1.7 but run on 1.8. No problem, your programm will work as needed.
JDK 7 as system default : You compile in 1.7 version and run on the same version.
I am wondering in waht case you would like to use the first case ?
truck load of difference actually. With JDKs the the compliance level is a directive to the compiler to specifically use the optimizations and linking features for the version you specified. It has a lot more going under the hood but I don't think you want to know that. New JDK versions bring new features and the compilers in those version are able to understand and link those features when building class files or assembled code of your source Java files. Consequently the JVM runtime in those JDKS is also equipped to handle such optimizations and cases and process them. So without compliance levels the class file that you build with JDK8 would only run correctly with JDK8 based runtimes. They may not do so with JDK7 or 6. To counter this problem and thus allow your JDK8 compiled code to run on JDK8,7 and maybe even on 6, hyou need to add compliance level to compiler directives accordingly. Downside is that you may not be able to use some latest features which the compiler offers but such cases are far few and outweigh the need for interoperability and potability.
I created a Jar with Java 6. Now I'm creating a release document and recording the dependencies. How can I find the earliest version of Java that can successfully run the Jar, and the earliest version of Java that can successfully compile the source into a Jar?
I only know a manuel solution: try it out. There are, however, two things to consider.
For which version is the code language compatible?
For which JRE will it execute?
The first you can do with your current JDK, just iterate over the -source and -target arguments which you pass to your javac compiler. This will, however, not prevent you from using classes and methods from the JDK you are using. If you do, the code will not execute for a lower JRE, if you are using classes or methods that where not present back then.
The savest way would be to install all different JDKs along and try to compile the code with each of their compilers.
If you created the jar with java 6 and did not specify a different version of output bytecode, the generated class files will require Java 6 or greater. You can experiment to see what versions of bytecode you can generate with your source with the -target command line option if you're compiling manually, if you're using eclipse or some other IDE, most have settings that control the generated bytecode version in project options or somewhere similar.
A related post about determining the bytecode versions of class files: What version of javac built my jar?
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.
Is there any way to restrict a java project configured with 1.4 compiler compliance level from using 1.5/1.6 specific API where the system is playing with 1.6 JRE?
Setting only 1.4 compiler compliance level on my project does not warn me from using especially 1.5/1.6 specific java.lang.String methods.
Make sure that you're also using an 1.4 JRE system library in your project. The 1.4 compiler settings change the features which will be written into the class files, it doesn't restrict what you can see from the libraries in your project.
Open the properties for your project and check what you find under "Java Build Path" -> "Libraries" -> "JRE System Library". Click "Edit" to change or add new Java versions.
I would suggest the safest (and perhaps the simplest) way is to compile using a JDK 1.4, and run using a JRE 1.6.
You will need the rt.jar from a 1.4 JRE (for instance by having the 1.4 JRE installed). If using javac use the following options:
javac -source 1.4 -target 1.4 -bootclasspath /path/to/j2se1.4/lib/rt.jar [...]
Using javac from a later JRE means that it should have fewer bugs for old source, although it may not necessarily be entirely "bug compatible".
You might also want to use -Djava.ext.dirs=directories and -Djava.endorsed.dirs, but putting things in those directories is generally a bad idea.
Note: Even 1.5 has finished its End Of Service Life period. Get with 1.6! (Or buy one of our excellent Java for Business contracts...)
What I would do is to remove those JDKs/JREs, which you don't want to use, from Eclipse's settings. In your case that would be 1.5 and 1.6.
To do this, go to Windows->Preferences and then type "JRE" in the top left hand side.
I would also check which JRE Eclipse in launched in and maybe edit your eclipse.ini.
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.