When using the Java compiler (javac), we can specify two kinds of compatibility. One is using -source and the other is using -target. What is the difference between these two?
For example, -source 1.5 and -target 1.6?
Also, is there any case where we use a different source and target compatibility level?
From the javac docs:
-source Specifies the version of source code accepted.
-target Generate class files that target a specified version of the VM. Class files will run on the specified target and on later versions, but not on earlier versions of the VM.
In your example:
-source 1.5 and -target 1.6
This would be used to make sure that the source code is compatible with JDK 1.5, but should generate class files for use on JDK 1.6 and later.
Quite why you would do this is another matter.
The -source indicates what level of compliance your source code has: are you using Annotations? Then you would need at least 1.5; are you using #override on interface implementations, you would need 1.6 etc
The -target specifies what Java version you want to be able to run your classes on. You could use a Java SE 7 compiler and compile to run on Java SE 1.5.
This is mostly useful to produce a jar file working with an older version of Java.
I believe that so far all JDKs are able to execute older version too, so there is no real reason to have target larger than source.
It does however make sense to set target to e.g. 1.6 when using a 1.7 JDK.
I'm not sure, but I believe it could work in some situations to compile a 1.7 java code using a 1.7 compiler to a 1.6 jar, for example expressions such as
ArrayList<Integer> foo = new ArrayList<>();
that are only valid in 1.7+ source version should compile to 1.6 compatible byte code. But I have not verified whether the compiler will actually do this. Unfortunately, this doesn't seem to be implemented in practise.
Related
I am posting this question as I have limited resources to test or confirm by my own.
Problem statement : I have an old custom Jar working with java 1.6. We don't have source code as well.
To fix one of the issue we updated 2 .class file of this jar using 7-zip.The code change was just to update the existing loggers with more meaningful messages.
These 2 .class file compiled with java 1.7.
Now the questions is whether this jar will work correctly with java 1.6 or will generate java version issue as 2 of the .class files are compiled with java 1.7.
Note - I know it's easy to test this rather then asking here but my test environment is only supporting java 1.8 and it's working perfectly but whether it will run with java 1.6 or not I can't test.
If you compile using: javac Foo.java where that javac is from a JDK1.7 installation, and you then attempt to run the class file that results on a JDK1.6, it will not work.
However, all you need to do is this:
javac -source 1.6 -target 1.6 Foo.java, and then it will, unless you used features from 1.7. If you use language features (I can't think of any, so I doubt it), it won't compile, and thus you know. If you use API, it will compile and you won't know. There is no easy solution to this other than compiling with JDK1.6 (or compiling with javac7 against a bootcp of JDK1.6, but you need to download and install a JDK1.6 to get that; might as well just use javac6 then).
Binary Compatibility
The class file version for Java SE 7 is 51, as per the JVM Specification, because of the invokedynamic byte code introduced by JSR 292. Version 51 class files produced by the Java SE 7 compiler cannot be used in Java SE 6.
Java SE 7 is binary-compatible with Java SE 6 except for the incompatibilities . Except for the noted incompatibilities, class files built with the Java SE 6 compiler will run correctly in Java SE 7.
Friends Words ...
The compiler is not backwards compatible because bytecode generated with Java7 JDK won't run in Java 1.6 jvm (unless compiled with the -target 1.6 flag). But the JVM is backwards compatible, as it can run older bytecodes.
So they chose to consider the compatibility from the point of view of javac (as it is the part specific to the JDK), meaning that the bytecode generated can be run in future releases of the jvm (that is more related to the JRE, but also bundled in the JDK).
In brief, we can say:
JDK's are (usually) forward compatible.
JRE's are (usually) backward compatible.
Java Says
Cross-Compilation Options
By default, classes are compiled against the bootstrap and extension classes of the platform that javac shipped with. But javac also supports cross-compiling, where classes are compiled against a bootstrap and extension classes of a different Java platform implementation. It is important to use -bootclasspath and -extdirs when cross-compiling; see Cross-Compilation Example below.
-target version
Generate class files that target a specified version of the VM. Class files will run on the specified target and on later versions, but not on earlier versions of the VM. Valid targets are 1.1, 1.2, 1.3, 1.4, 1.5 (also 5), 1.6 (also 6), and 1.7 (also 7).
The default for -target depends on the value of -source:
If -source is not specified, the value of -target is 1.7
If -source is 1.2, the value of -target is 1.4
If -source is 1.3, the value of -target is 1.4
If -source is 1.5, the value of -target is 1.7
If -source is 1.6, the value of -target is 1.7
For all other values of -source, the value of -target is the value of -source.
-bootclasspath bootclasspath
Cross-compile against the specified set of boot classes. As with the user class path, boot class path entries are separated by colons (:) and can be directories, JAR archives, or ZIP archives.
For More about Cross-Compilation look at
http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#crosscomp-options
Better than me at
http://www.oracle.com/technetwork/java/javase/compatibility-417013.html
The code base for Java 7 and versions of Java 6 is very similar and even shares many of the same bugs. e.g. the was a well known bug in Java 7 when it was released to do with loop optimisation and people suggested waiting for it to be fixed before migrating. The interesting thing was the bug was also in Java 6, the only difference was that the optimisation was on by default in Java 7 and off by default in Java 6.
Most of the performance improvements in Java 7 were back ported into Java 6.
From the docs
-source release Specifies the version of source code accepted.
If I have a JDK version , say , 1.8, and I mention -source=1.6 , what does it mean ? Does this only mean that whatever code I have written can be compiled by javac of JDK 1.6 or above ?
If that be case , why pass -source=1.6 during javac command ? As this will generate .class files and hence there is no source code left to mark (the source code compatibility to 1.6 or above) ? After javac command, all we get is the bytecode and no .java files.
Does this only mean that whatever code I have written can be compiled by javac of JDK 1.6 or above ?
Nope.
The -source=1.6 option means that your code can only use Java language constructs that are part of the Java 6 and earlier versions of the Java language.
For example, any Java 8 lambdas, or Java 9 var declarations would be flagged as compilation errors.
Java8 introduced lambda expressions. If you compile your application with -source=1.6 the compiler will not allow lambda expressions despite it being supported with JDK8.
If I have a JDK version , say , 1.8, and I mention -source=1.6 , what does it mean ? Does this only mean that whatever code I have written can be compiled by javac of JDK 1.6 or above ?
If your code uses Java 8 features, it won't even compile with -source=1.6. Otherwise, not necessarily true, but generally, yes, it should work with Java 6 and above.
If that be case , why pass -source=1.6 during javac command ? As this will generate .class files and hence there is no source code left to mark (the source code compatibility to 1.6 or above) ? After javac command, all we get is the bytecode and no .java files.
Javac is the Java Compiler. Of course it will generate .class files, as that is the compiled form of a java program. Why would you pass it? Let's say you want to target a specific version, this is the easiest way to keep support at that level.
You can test this by using a Java 8 feature ( lambdas, streams, datetimeformatter ), then try to compile. Your compilation will fail.
By specifying the source argument on the compiler, you are telling the compiler that you want the source code you are submitting to comply with that version of Java and check against specific language features for the version you selected (The default is the newest version typically, even if you don't specify source version yourself). The docs are clear on what values are acceptable and what value is the default. This does not change your source code or transform your code to older versions, it merely alerts you if you are using features that are in later versions of Java. If you are not using newer features of the java language then this will simply compile your code and generate class files as usual.
javac MyProgram.java -source 1.6
The command above will tell the compiler to treat the source code as it was compatible with Java version 1.6.
Below are the allowable values for JDK 1.8 and the description from the docs.
1.3 The compiler does not support assertions, generics, or other language features introduced after Java SE 1.3.
1.4 The compiler accepts code containing assertions, which were introduced in Java SE 1.4.
1.5 The compiler accepts code containing generics and other language features introduced in Java SE 5.
5 Synonym for 1.5.
1.6 No language changes were introduced in Java SE 6. However, encoding errors in source files are now reported as errors instead of warnings as in earlier releases of Java Platform, Standard Edition.
6 Synonym for 1.6.
1.7 The compiler accepts code with features introduced in Java SE 7.
7 Synonym for 1.7.
1.8 This is the default value. The compiler accepts code with features introduced in Java SE 8.
8 Synonym for 1.8.
I try to find a use case when it is necessary to use a special target version of the compiler javac using the target parameter.
Java is backward compatible, isn't it? So, if I compile a hello world program with version 11, it could run on a JVM with version 8, or?
The only use case I could imagine is, when you have dependencies (other jars) which are compiled in a certain version and you have to match this special version when compiling the own code.
Java is backward compatible, isn't it? So, if I compile a hello world program with version 11, it could run on a JVM with version 8
That is exactly backward. If you have a version of a Java class compiled with version 8, Java 11 is backwards compatible and can run it. The reverse is not backwards compatibility, and is the purpose of the --target command line flag. Specifically so a class compiled by the Java 11 compiler can run on Java 8. Without that, you would get an java.lang.UnsupportedClassVersionError: Unsupported major.minor version
By default, classes are compiled against the bootstrap and extension classes of the platform that javac shipped with. But javac also supports cross-compiling.
-target version
Generate class files that target a specified version of the VM. Class
files will run on the specified target and on later versions, but not
on earlier versions of the VM. Valid targets are 1.1, 1.2, 1.3, 1.4,
1.5 (also 5), 1.6 (also 6), and 1.7 (also 7) ... . The default for -target depends on the value of -source:
If -source is not specified, the value of -target is 1.7
If -source is 1.2, the value of -target is 1.4
If -source is 1.3, the value of -target is 1.4
If -source is 1.5, the value of -target is 1.7
If -source is 1.6, the value of -target is 1.7
For all other values of -source, the value of -target is the value of -source.
Refer javadoc for more details here.
I try to find a use case when it is necessary to use a special target version of the compiler javac using the target parameter.
A common scenario is roughly:
production system runs your Java program
individual engineers write/maintain that Java program
JDK versions on engineers' laptops is newer than JDK on production system
Using the "target" flag, it's possible for the engineers to write software using newer/later JDK versions, while still being able to run it on an older JDK (on a production system).
And "older" really doesn't need to be all that old... for example, a production system might be running in Amazon using the latest version of Corretto (which is currently 18, with all other available versions being 8, 11, and 17), and individual engineers could have JDK 19 or 20 on their machines.
The Java Language Spec discusses this topic in Chapter 13, Binary Compatibility:
This chapter specifies minimum standards for binary compatibility guaranteed by all implementations. The Java programming language guarantees compatibility when binaries of classes and interfaces are mixed that are not known to be from compatible sources, but whose sources have been modified in the compatible ways described here.
And various more specific comments in JLS 13.2, What Binary Compatibility Is and Is Not:
A change to a type is binary compatible with (equivalently, does not break binary compatibility with) pre-existing binaries if pre-existing binaries that previously linked without error will continue to link without error.
Note: I posted links specifically from JLS version 1.8 as it's relevant to versions in this question (8 and 11), but Java's treatment of binary compatibility hasn't changed
in prior or subsequent versions.
I am required to compile my source code with Java 6 for university. I currently have Java 8 JDK installed on my PC (Linux). How do I compile in Java 6?
p.s. I understand I wont be able to use any Java 7 or 8 features.
p.p.s. I realize java 6 source code will compile fine using the Java 8 JDK however I find that I am accidentally using post Java 6 features in my programs when compiling at home. When I try to compile the source code on the lab computers at uni I run into a bunch of errors. e.g. not being able to cast an object to a primitive.
The way to fix that problem is to compile using the later SDK and use the cross compilation options when compiling.
Cross-Compilation Options
By default, classes are compiled against the bootstrap and extension classes of the platform that javac shipped with. But javac also supports cross-compiling, where classes are compiled against a bootstrap and extension classes of a different Java platform implementation. It is important to use the -bootclasspath and -extdirs options when cross-compiling.
-target version
Generates class files that target a specified release of the virtual machine. Class files will run on the specified target and on later releases, but not on earlier releases of the JVM. Valid targets are 1.1, 1.2, 1.3, 1.4, 1.5 (also 5), 1.6 (also 6), 1.7 (also 7), and 1.8 (also 8).
The default for the -target option depends on the value of the -source option:
If the -source option is not specified, then the value of the -target option is 1.8
If the -source option is 1.2, then the value of the -target option is 1.4
If the -source option is 1.3, then the value of the -target option is 1.4
If the -source option is 1.5, then the value of the -target option is 1.8
If the -source option is 1.6, then the value of the -target is option 1.8
If the -source option is 1.7, then the value of the -target is option 1.8
For all other values of the -source option, the value of the -target option is the value of the -source option.
-bootclasspath bootclasspath
Cross-compiles against the specified set of boot classes. As with the user class path, boot class path entries are separated by colons (:) and can be directories, JAR archives, or ZIP archives.
To use the options completely correctly (i.e. using also the -bootclasspath option) requires the rt.jar of a JRE (not JDK) of the target version.
You can use the below for compilation
% javac -target 1.6 <whatever you want to compile>
I had a similar issue where a part of the project wanted to use Java 6 and rest of the project needed Java 8. You can change java version in just one terminal window for this purpose.
To change the java version in just one terminal window without changing the profile, use:
export JAVA_HOME=$(/usr/libexec/java_home -v 1.6)
I have installed many versions of the JDK: 1.4.2, 1.5 and 1.6.
How do I specify which version of the JDK is used when compiling using Ant?
Two solutions:
Specify the full path in your command:
for example /opt/java/jdk16/bin/javac ... on Linux
Use the -source and -target arguments of the javac command. This allows you specify the source code level and targeted JRE version
Also note:
Some Linux distributions can include tools to specify which JDK version to use by default.
Using -source and -target checkes that your language constructs are compliant with the targeted runtime, but does NOT check that core classes are compatible. This means that compiling with -source 1.4 on a JDK 1.6 will be just fine, even if you use String.isEmpty() which appeared in Java 6. This might lead to errors at runtime
javac -source 1.4 -target 1.4 YourFile.java
-source release
Specifies the version of source code accepted. The following
values for release are allowed:
1.3 the compiler does not support assertions, generics, or other
language features introduced after JDK 1.3.
1.4 the compiler accepts code containing assertions, which were
introduced in JDK 1.4.
1.5 the compiler accepts code containing generics and other
language features introduced in JDK 5. The compiler defaults to the
version 5 behavior if the -source flag is not used.
5 Synonym for 1.5
Here is the relevant documentation.
http://download.oracle.com/javase/1,5.0/docs/tooldocs/windows/javac.html
Use the Ant <javac> task<source> and/or <target> attributes. Valid values can be from 1.1 to 1.7, with 5, 6, and 7 valid aliases for 1.5, 1.6 and 1.7. Also, the <executable> attribute can be used to set which java javac compiler is used. For example:
<javac source="1.4" target="1.4" executable="c:\java1.6\bin\javac.exe"/>