运行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目录下存在同名的文件,产生编译错误。
如果在d:\\com或d:\\src\\com目录下存在Test.class类文件,则不进行编译,Main.class将直接关联该目录下的类文件。
如果在d:\\com和d:\\src\\con目录下都存在类文件,则编译时选择类路名参考表中靠前的项,比如Main.java中删除import org.*:
如果 javac –classpath ..;. Main.java,将采用的是d:\\com目录下的Test.class
如果javac –classpath .;.. Main.java,将采用的是d:\\src目录下的Test.class
运行程序时对类的查找过程同编译时的过程,只不过运行程序时在查找过程中是直接查找类文件而不是源文件,如果类文件查找不到,则返回NoClassDefFoundError错误。不管是在编译还是运行时因为同名问题造成了这么多的困扰,所以尽量不要在不同的路径下出现同名文件。