JDK版本不一致问题
原文:http://www.blogjava.net/super-nini/archive/2009/08/06/290085.html
01.
package
com.unmi;
02.
03.
public
class
HelloWorld
04.
{
05.
public
static
void
main(String[] args)
06.
{
07.
System.out.println(
"Hello, World!"
);
08.
}
09.
}
用 JDK 1.5 的 javac -d . HelloWorld.java 编译出来的字节码 HelloWorld.class 用 UltraEdit 打开来的内容如图所示:
2)命令 javap -verbose
对于编译出的 class 文件用 javap -verbose 能显示出类的 major.minor 版本,见下图:
JDK 编译器版本 | target 参数 | 十六进制 minor.major | 十进制 minor.major |
jdk1.1.8 | 不能带 target 参数 | 00 03 00 2D | 45.3 |
jdk1.2.2 | 不带(默认为 -target 1.1) | 00 03 00 2D | 45.3 |
jdk1.2.2 | -target 1.2 | 00 00 00 2E | 46.0 |
jdk1.3.1_19 | 不带(默认为 -target 1.1) | 00 03 00 2D | 45.3 |
jdk1.3.1_19 | -target 1.3 | 00 00 00 2F | 47.0 |
j2sdk1.4.2_10 | 不带(默认为 -target 1.2) | 00 00 00 2E | 46.0 |
j2sdk1.4.2_10 | -target 1.4 | 00 00 00 30 | 48.0 |
jdk1.5.0_11 | 不带(默认为 -target 1.5) | 00 00 00 31 | 49.0 |
jdk1.5.0_11 | -target 1.4 -source 1.4 | 00 00 00 30 | 48.0 |
jdk1.6.0_01 | 不带(默认为 -target 1.6) | 00 00 00 32 | 50.0 |
jdk1.6.0_01 | -target 1.5 | 00 00 00 31 | 49.0 |
jdk1.6.0_01 | -target 1.4 -source 1.4 | 00 00 00 30 | 48.0 |
jdk1.7.0 | 不带(默认为 -target 1.6) | 00 00 00 32 | 50.0 |
jdk1.7.0 | -target 1.7 | 00 00 00 33 | 51.0 |
jdk1.7.0 | -target 1.4 -source 1.4 | 00 00 00 30 | 48.0 |
Apache Harmony 5.0M3 | 不带(默认为 -target 1.2) | 00 00 00 2E | 46.0 |
Apache Harmony 5.0M3 | -target 1.4 | 00 00 00 30 | 48.0 |
自已设定编译选项,你会看到选择不同的 compiler compliance level 是,Generated class files
compatibility 和 Source compatibility
也在变,你也可以手动调整那两项,手动设置后你就不用很在乎用的什么版本的编译器了,只要求他生成我们希望的字节码就行了,再引申一下就是即使源代码是用
VB 写的,只要能编译成 JVM 能执行的字节码都不打紧。在其他的 IDE 也能找到相应的设置对话框的。
其他时候,你一定要知道当前的 JVM 是什么版本,能接受的字节码主版本号是多少(可对照前面那个表)。获息当前 JVM 版本有两种途径:
第一:如果你是直接用 java 命令在控制台执行程序,可以用 java -version 查看当前的 JVM 版本,然后确定能接受的 class 文件版本
第二:如果是在容器中执行,而不能明确知道会使用哪个
JVM,那么可以在容器中执行的程序中加入代码 System.getProperty("java.runtime.version"); 或
System.getProperty("java.class.version"),获得 JVM 版本和能接受的 class 的版本号。
最后一绝招,如果你不想针对低版本的 JVM 用 target 参数重新编译所有代码;如果你仍然想继续在代码中用新的 API
的话;更有甚者,你还用了 JDK 1.5 的新特性,譬如泛型、自动拆装箱、枚举等的话,那你用 -target 1.4 -source 1.4
就没法编译通过,不得不重新整理代码。那么告诉你最后一招,不需要再从源代码着手,直接转换你所正常编译出的字节码,继续享用那些新的特性,新的
API,那就是:请参考之前的一篇日志:Retrotranslator让你用JDK1.5的特性写出的代码能在JVM1.4中运行,我就是这么用的,做好测试就不会有问题的。
七:再议一个实际发生的相关问题
这是一个因为拷贝 Tomcat 而产生的 Unsupported major.minor version 49.0 错误。情景是:我本地安装的是
JDK 1.5,然后在网上找了一个 EXE 的 Tomcat 安装文件安装了并且可用。后来同事要一个
Tomcat,不想下载或安装,于是根据我以往的经验是把我的 Tomcat 整个目录拷给他应该就行了,结果是拿到他那里浏览 jsp 文件都出现
Unsupported major.minor version 49.0 错误,可以确定的是他安装的是 1.4 的
JDK,但我还是有些纳闷,先前对这个问题还颇有信心的我傻眼了。惯性思维是编译好的 class 文件拿到低版本的 JVM
会出现如是异常,可现并没有用已 JDK 1.5 编译好的类要执行啊。
后来仔细看异常信息,终于发现了 %TOMCAT_HOME%\common\lib\tools.jar 这一眉目,因为 jsp
文件需要依赖它来编译,打来这个 tools.jar 中的一个 class 文件来看看,49.0,很快我就明白原来这个文件是在我的机器上安装
Tomcat 时由 Tomcat 安装程序从 %JDK1.5%\lib 目录拷到 Tomcat 的 lib 目录去的,造成在同事机器上编译
JSP 时是 1.4 的 JVM 配搭着 49.0 的 tools.jar,那能不出错,于是找来 1.4 JDK 的 tools.jar
替换了 Tomcat 的就 OK 啦。
八:小结
其实理解 major.minor 就像是我们可以这么想像,同样是微软件的程序,32 位的应用程序不能拿到 16 位系统中执行那样。
如果我们发布前了解到目标 JVM 版本,知道怎么从 java class 文件中看出 major.minor 版本来,就不用等到服务器报出异常才着手去解决,也就能预知到可能发生的问题。
其他时候遇到这个问题应具体解决,总之问题的根由是低版本的 JVM 无法加载高版本的 class 文件造成的,找到高版本的 class 文件处理一下就行了。