|
运行JAVA程序时,经常碰到一些莫名其妙的问题,运行结果与预期不一致,这类问题的定位非常麻烦,很多的情况下是由于在不同的路径下存在同名的文件,那么在JAVA程序运行时是如何定位类文件的呢?如何解决同名类问题?首先,我们从JAVA程序的编译说起,JAVA运行时的类查找机制同编译时,只不过是运行时所有的源文件已经被编译成了类文件。
对JAVA源代码进行编译时分分成三个步骤: 第一:形成三个表,分别是类路径参考表表、完整限定类参考表、和通配类参考表。 第二:根据三个表去查找类。 第三:对查找到的类进行编译,回到第二步。 实际第二步和第三步是同时进行的,以下合称第二步。
以如下目录结构为例对以上过程进行说明: D:\ | —src | | | —Main.java, | | | —Test.java | | | | | —com | | | | | —Test.java | | | —org | | | —Test.java | —com | —Test.java
Main.java
import com.*; import org.*; import org.Test; 1.class Main 2.{ 3. public static void main(String[] args) 4. { 5. org.Test t1 = new org.Test(); 6. Test t2 = new Test(); 7. } 8.} d:\src\com\Test.java package com; class Test { public Test() { System.out.println(“Now in com of innter”); } } d:\src\org\Test.java package org; class Test { public Test() { System.out.println(“Now in org ”); } }
d:\com\Test.java package com; class Test { public Test() { System.out.println(“Now in com of outer ”); } }
运行:javac Main.java,下面对类的定位过程进行分析 第一步:根据环境变量CLASSPATH形成类路径参考表,如果没有设置该环境变量,则根据-classpath的值形成类路径参考表,如果没有-classpath选项,则该表中只有一项,如下所示: 序号 路径 1 · 完整限定类参考表根据import中的全名称项形成,则当前的完整限定类参考表如下: 序号 完整限定类 1 org.Test 通配限定类参考表根据import中的通配名称形成,则当前通配限定名称参考表如下: 序号 通配类 1 com.*; 2 org.*;
第二步,根据三个表查找类的过程如下: 1. 检查类是否是全限定名称,如果不是转2,如果是则把全限定名称与类路径参考表组合,到相应的目录下查找是否存在该类的源文件,则编译该源文件形成类文件,如果源文件也不存在则报编译错误 2. 查找全限定名称表,看是否存在与同名的类,如果存在两个及以上同名类,则报编译错误,如果仅存在一个,则组合类路径参考表和该类在全限定名称表中的全名称到相应目录下查找,如果存在源文件,则编译该类的源文件,否则转3。 3. 先在当前路径查找是否存在该类,如果没有类源文件则转4,否则编译该文件 4. 组合类路径参考表和通配类参考表,到相应的目录下查找是否存在该类,如果在一个目录下存在两个及以上源文件,则报编译错误,如果只有一个该类源文件,则对该源文件进行编译,如果没有找到则报编译错误。
※:以上步骤基于目前只有源文件而没有类文件的假设,如果在某些目录下已存在类文件,除以上步骤中出现编译错误的情况外,如果发现有类文件,则结束查找,编译成功。
根据以上原则对例子进行分析: javac Main.java 第5行:根据步骤1,能够在.\org下找到Test.java文件,则对Test.java进行编译形成Test.class 第6行:根据步骤2,在全限定名称表中存在org.Test,然后在.\org下找该了Test.java,则对该目录下的Test.java进行编译。
如果对Main.java进行如下修改,去掉import org.Test; import com.*; import org.*; 1.class Main 2.{ 3. public static void main(String[] args) 4. { 5. org.Test t1 = new org.Test(); 6. Test t2 = new Test(); 7. } 8.} java Main.java 第6行,根据步骤三,在当前目录找到了Test.java,则对当前目录的Test.java进行编译。
如果删除当前目录下的Test.java,再编译: javac Main.java 第6行,根据步骤4,在.\com和.\org目录下存在同名的文件,产生编译错误。
如果import org.*,并在编译时增加如下的classpath选项: javac –classpath .;.. Main.java 如果两个目录下都没有类文件,则根据步骤四,将编译.\com下的Test.java。
如果修改classpath选项: javac –classpath ..;. Main.java 如果两个目录下都没有源文件,则根据步骤四将编译 d:\com下的Test.java。
如果在d:\com或d:\src\com目录下存在Test.class类文件,则不进行编译,Main.class将直接关联该目录下的类文件。
如果在d:\com和d:\src\con目录下都存在类文件,则编译时选择类路名参考表中靠前的项。 如果 javac –classpath ..;. Main.java,将采用的是d:\com目录下的Test.class 如果javac –classpath .;.. Main.java,将采用的是d:\src目录下的Test.class
运行程序时对类的查找过程同编译时的过程,只不过运行程序时在查找过程中是直接查找类文件而不是源文件,如果类文件查找不到,则返回NoClassDefFoundError错误。
不管是在编译还是运行时因为同名问题造成了这么多的困扰,所以尽量不要在不同的路径下出现同名文件。
|
一共有 6 条评论
收录个人主页博客.
更多博客更多精彩.欢迎登录
__________________________________________________________
古语有云"人各有志,不可相强",也有"道不同不足与谋",你当然可以选择环境,你如果够血性,你就辞职,三天之内我肯定放你走人,你也可以去找你心目中理想的公司和上司.否则,在这里发发牢骚没什么意思,还不如把自己手上的事情做好,或者如1楼唐僧所说多照顾照顾你的家庭.
对于员工加班的补偿至少应是等价的,而不是说,你加3天班,可以休息一天,或是如同打发要饭的,给点点钱就了事,这是对人的极为不尊重。作为员工的我们,虽然不能改变环境,但是我们可以选择环境。