今天在Windows下安装JDK8时遇到以下问题,原本机子上安装了JDK7,正常步骤安装JDK8以后
java -version时输出
javac -version时正常
输出1.7
环境变量如下
解决办法,regedit->按上述路径->修改CurrentVersion到1.7
java -version输出
javac -version正常
这涉及到很多问题,包括了JDK7与JDK8安装的区别,JRE与JDK的区别,环境变量与注册表中的项分别有什么用,以及java.exe做了什么事,最后还有为什么这里java与javac不同,我尝试简单的分析一下这些问题。
在命令行输入java命令之后,系统如何找到java.exe,这是由PATH环境变量决定的,在任意目录下输入where java,就可以得到java.exe的路径
本机的PATH变量设置下结果如图
那么这两个java.exe代表了什么?
JDK7与JDK8安装的区别
首先这两个java.exe都来自于之前JDK7的安装,与JDK8无关,为什么呢,通过PATH环境变量可知,系统在查找java.exe的时候,目录包括了system32和jdk1.7及其他,并没有jdk1.8相关的目录,后一个java.exe来自于jdk1.7目录很好理解,而前一个java.exe同样来自于JDK7安装,而且看网上资料有人说删掉这个java.exe后重启又会出现,说明Oracle很想要这个java.exe待在这里,放在这里的主要目的是保证用户在没有设置环境变量的情况下依旧可以通过java.exe运行Java程序(这里涉及到了JRE与JDK的区别,见后文)。
而第一个问题JDK8与JDK7安装的差别也就在这里,JDK8不会在system32里放置java.exe,javaw.exe,javaws.exe,所以在只安装了JDK8未做任何设置的情况下,应该是无法执行java命令的,那么JDK8就只安装了jdk1.8的文件夹和jre1.8么,也不是,在 C:\ProgramData\Oracle\Java\javapath路径下可以找到JDK8版本的java.exe,javaw.exe,javaws.exe,只不过由于这个目录并不在PATH变量下,所以命令行中java命令无法找到这里,当你把这个目录添加到PATH之后,就可以找到了。
JDK与JRE的区别
上面提到,JDK7在system32中设置java.exe,javaw.exe,javaws.exe是为了用户在不设置环境变量的情况下就可以执行java命令,那么这里的java命令是怎么执行的呢,首先你要知道的是这里的java命令和安装的jdk1.7目录完全没有关系(基本上是这样,可以参看后文java.exe做了什么),它最终会找到的是jre1.7目录,这个目录也就代表了JRE,也就是说运行Java程序只需要有JRE(Java Runtime Environment)就够了,而JDK(Java Development Kit)是为了开发Java程序而存在的,而且JDK内部包含了一套JRE,与外部的JRE是一致的(外部JRE在安装JDK时可选),那么JDK内部的JRE又有什么用,因为java,javac等命令最终是java实现的,所以执行javac等命令需要JRE。
由此可知,JDK包含了JRE,除去JRE还有各类Java的开发工具。安装JDK时,JDK内部会自带一套JRE,安装时也可以选择安装一套公共的JRE,也就是jre1.7目录,这个目录与jdk1.7同级。JDK8的安装也一样,可选一套公共的JRE,和一套内部自带的JRE。
环境变量与注册表项
回到java命令的执行,很明显环境变量的设置保证了我们能找到java.exe以及根据环境变量的顺序找到哪一个java.exe(永远找到PATH变量前面包含的那个),所以在本机的环境变量设置下,只能找到system32以及jdk1.7\bin下的两个java.exe,并没有找到jre1.7\bin以及jdk1.7内部jre\bin里的两个java.exe,所以更不会知道jdk1.8\bin、jdk1.8\jre\bin\、jre1.8\bin、以及C:\ProgramData\Oracle\Java\javapath这四个目录下的java.exe,而本机在执行的时候选择了system32目录下的java.exe。
那么注册表项HKEY_LCOAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\CurrentVersion又是干什么的,这就要涉及到java.exe干了什么。
java.exe干了什么
这个问题有点大,现在只关注与开始的一小部分,首先我们要知道的是所有的Java程序都是跑在JVM(Java Virtual Machine)Java虚拟机上的,JVM才是Java程序可以一次编写到处运行的关键,它为我们开发Java程序屏蔽了操作系统等底层的差异,所以可以把虚拟机理解为操作系统之上为Java程序准备的Java操作系统,任何平台编写的Java代码在编译生成字节码后可以在任何平台的JVM上运行。
那么java.exe最终是要把编译生成的xxx.class放到JVM上运行的,那么它第一件要做的事情肯定是找到JRE,因为JVM是JRE的一部分,JRE出了java等命令外,还包括了JVM(jre\bin\server或者jre\bin\client等),当然也有Java SE 的基础类库等(rt.jar)。它之后做的事情就比较复杂了,这里就不讨论了。
那么java.exe如何找到JRE呢,顺序如下
(1)当前目录下是否是JRE目录下的bin,适用于JRE\bin目录下的java.exe
(2)父目录下是否存在JRE目录,适用于JDK\bin目录下的java.exe
(3)查询注册表里的Java Runtime Environment的信息,适用于system32以及C:\ProgramData\Oracle\Java\javapath下的java.exe
如果都找不到,报错。
而文章开头我遇到的问题也就来自于(3),问题在哪里
关键在于java.exe需要与JRE的版本一致,(1)和(2)基本不会遇到这个问题,除非你把JRE8里的java.exe移到JRE7\bin目录下。而我的问题在于
system32下的java.exe是1.7版本的,它在寻找JRE时问注册表,由于刚安装了JDK8,安装时注册表的CurrentVersion项被修改为1.8,指向了jre1.8目录,所以1.7的java.exe与1.8的jre出现了版本冲突,所以修改CurrentVersion到1.7,又可以匹配了,问题解决。
java与javac在这个问题的差别
javac只存在于jdk\bin目录,当它查找JRE时会找到自己jdk目录下的JRE,很明显不会出现版本不匹配的问题
java在system32目录,在安装了JDK8以后注册表里的信息进行了更新,1.7版本的java.exe也就查找到了1.8版本的JRE,所以出现了文章开头的问题。
延伸阅读: Java深度历险