日志文章

2007年08月31日 15:39:45

JAVA编译和运行时的类定位机制

运行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:\comd:\src\com目录下存在Test.class类文件,则不进行编译,Main.class将直接关联该目录下的类文件。


如果在
d:\comd:\src\con目录下都存在类文件,则编译时选择类路名参考表中靠前的项。
如果 javac –classpath ..;. Main.java,将采用的是d:\com目录下的Test.class
如果javac –classpath .;.. Main.java,将采用的是d:\src目录下的Test.class


运行程序时对类的查找过程同编译时的过程,只不过运行程序时在查找过程中是直接查找类文件而不是源文件,如果类文件查找不到,则返回
NoClassDefFoundError错误。


不管是在编译还是运行时因为同名问题造成了这么多的困扰,所以尽量不要在不同的路径下出现同名文件。

类别: 无分类 |  评论(6) |  浏览(5841) |  收藏
一共有 6 条评论
6楼 [匿名]mmj5博客导航 2007年10月13日 13:24:08 Says:
mmj5博客导航 http://www.mmj5.com
收录个人主页博客.
更多博客更多精彩.欢迎登录
5楼 [匿名]咋的 2007年09月16日 17:21:23 Says:
咋的,还不让人说说啊,我又没有占用上班时间!辞职和血性有关系么?你要有血性,就不应该这么对待你称为兄弟姐妹的员工!血性不是拿来说的,ok?
4楼 [楼主]张宝林 2007年09月15日 10:11:41 Says:
答:ALL
__________________________________________________________
古语有云"人各有志,不可相强",也有"道不同不足与谋",你当然可以选择环境,你如果够血性,你就辞职,三天之内我肯定放你走人,你也可以去找你心目中理想的公司和上司.否则,在这里发发牢骚没什么意思,还不如把自己手上的事情做好,或者如1楼唐僧所说多照顾照顾你的家庭.
3楼 [匿名]哈哈 2007年09月14日 16:37:14 Says:
终于有人受不了了来这反抗了,如果这些评论被删了,那会说明什么问题?
2楼 [匿名]匿名 2007年09月11日 13:04:20 Says:
加班不应当成为一个公司的风气,一个评价员工好坏的潜规则。加班应当是员工自发的,而不是公司的强迫行为(有形的或无形的)。
对于员工加班的补偿至少应是等价的,而不是说,你加3天班,可以休息一天,或是如同打发要饭的,给点点钱就了事,这是对人的极为不尊重。作为员工的我们,虽然不能改变环境,但是我们可以选择环境。
1楼 [匿名]唐僧 2007年09月10日 12:56:25 Says:
不能用加不加班来评价一个员工是否努力工作,上进心强否。要知道一个工作效率高,有工作热情的人也不见得就是每天加班。1种加班他真的在解决工作问题,他真的不会长此以往!因为工作是永远也做不完的,而他和他的家庭只有一个,除非你给他很多,否则对谁都是不应该的。1种是自我提升,既然是这样你又为什么要求他呢?还有一种是 玩。所以我说只要注重他的责任心,他的工作能力,不要一味强调加班。而且这个要求来自老总那里就变成一种剥削。
发表评论
看不清楚,换一张