Java之classpath学习
来源:优易学  2011-11-18 13:16:14   【优易学:中国教育考试门户网】   资料下载   IT书店

  1.class搜索路径的重要性
  理解class搜索路径对所有Java开发人员来说都很重要,但是,IDE的广泛使用掩盖了这项技术,使大家普遍对它缺乏了解,甚至包括好多老鸟。这个问题在开发分布式应用时尤其严重,因为应用程序运行时的系统环境可能和开发时的大不相同。
  本文详细描述了某些Java类被其他代码引用时,Java编译器和JVM如何使用类搜索路径定位这些类。这儿用一个非常简单的例子——同一个包中的两个类——来具体说明。我们将通过不同的方式来编译这两个类,根据classpath的设置不同,编译可能成功也可能失败。
  为了最清楚的说明这个问题,我们将只使用命令行工具进行编译。交互式开发工具有它们自己操作classpath的方法,这些方法因产品而异。
  至于是由Java编译器在编译时定位需要的类,还是由JVM在运行时来做,这两种方法没有本质的区别。但编译器可以从源代码中编译需要的类,而JVM不行。下面的例子中我们用编译器来做,但在运行时的实现也完全类似。
  2.实例
  本例有两个很小的类:com.web_tomorrow.CPTest1 和 com.web_tomorrow.CPTest2,如下所示:
  package com.web_tomorrow;
  public class CPTest1 {
  public static void main(String[] args) {
  System.out.println ("Run CPTest1.main()");
  }
  }
  package com.web_tomorrow;
  public class CPTest2 {
  public static void main(String[] args) {
  System.out.println ("Run CPTest2.main()");
  CPTest1 cpt1 = new CPTest1();
  }
  }
  Java代码组织的一个最基本规则就是`package name = directory name'(“包名 = 目录名”)。我们将为这两个类建立对应的目录结构,它们在包com.web_tomorrow中,所以我们创建目录 com/web_tomorrow来存放源代码:
  [root] com
  web_tomorrow
  CPTest1.java
  CPTest2.java
  在本文中青年人网提示用符号`[root]'来表示存放上述结构的任意目录,也就是说,根目录的位置由你决定。当然这会随安装这些文件的方式不同而不一样。
  3.基本原理
  让我们来尝试用命令行工具javac来编译CPTest1.java。为了完全禁止类搜索路径(以防已有的设置影响本例),我们在javac上加上选项`-classpath ""'。
  作为第一次试验,我们先换到CPTest1.java所在的目录下,并且尝试用javac和文件名进行编译。
  cd [root]/com/web_tomorrow
  javac -classpath "" CPTest1.java
  操作成功,因为编译器能发现CPTest1.java (它就在当前的工作目录下),并且CPTest1没有引用任何其他类。输出文件CPTest1.class在CPTest1.java的相同目录下,因为你没有给编译器任何信息让它作其它处理。到现在为止,一直都很好。现在让我们用同样的方式来试一下CPTest2。仍然在web_tomorrow目录下,执行命令:
  javac -classpath "" CPTest2.java
  虽然目录还是刚才的目录,CPTest1和CPTest2也在相同的包里,这一次却失败了。
  错误信息可能是这样的:
  CPTest2.java:7: cannot resolve symbol
  symbol : class CPTest1
  location: class com.web_tomorrow.CPTest2
  CPTest1 cpt1 = new CPTest1(); ^
  这一次和上一次成功的情况之间的区别之一就是CPTest2中有对CPTest1的引用:
  CPTest1 cpt1 = new CPTest1();
  这次发生了什么呢?当编译器遇到对CP1Test的引用时,它会认为CP1Test类与当前编译的CP2Test类在同一个包里。这个假定是正确的,于是编译器来寻找com.web_tomorrow.CP1Test,但它没有地方可以找,因为我们已经把类搜索路径
  明确的指定成了“”(也就是空)。
  你可能认为,只要告诉编译器在当前目录下寻找就可以解决这个问题。在Unix和Windows系统中,“当前目录”的标准符号都是一个点号(.),也就是这样的命令:
  javac -classpath "." CPTest2.java
  Fail Againt!跟刚才的例子完全一样,现在的问题是虽然CPTest1.java在当前目录下,但它实现的类不是CPTest1,而是com.web_tomorrow.CPTest1,编译器将在当前目录下寻找目录com/web_tomorrow。也就是说,它在目录
  [root]/com/web_tomorrow/com/web_tomorrow中寻找一个Java源文件或类文件,它们事实上根本不存在,于是出错。
  为了让编译器工作正常,我们不能让类搜索路径指向包含CPTest1的目录,而是按照Java标准中`package name = directory name'的规则,给类搜索路径指定编译器能找到CPTest1的根目录。这样才能工作,虽然不太好看:
  javac -classpath "../.." CPTest2.java
  在考虑如何变得不难看之前,看一下这个例子(仍在同一目录下)
  javac -classpath "" CPTest1.java CPTest2.java
  尽管classpath为空,这次却能工作。这是因为Java编译器会在命令行中明确列出的所有源代码中查找引用。如果要编译同一目录下的多个类,有一种很简单的方式:
  javac -classpath "" *.java
  '*.java'扩展为当前目录下所有.java文件的列表。这就说明为什么一次编译多个文件会成功,而同样的文件一个一个的编译却失败。
  编译CPTest2有一个更方便的方法:
  cd [root]
  javac -classpath "." com/web_tomorrow/CPTest2.java
  这次我们为CPtest2.java指定了完整的路径,而在-classpath选项中使用了'.'。另外,我们没有告诉编译器在当前目录下寻找文件,而是让它从当前目录开始搜索。
  因为我们要找的类是com.web_tomorrow.CPTest1,编译器将在./com/web_tomorrow(也就是说当前目录下的com/web_tomorrow子目录)下寻找。这才是CPTest1.java正确的位置。
  事实上,尽管我在命令行只指定了CPTest2,但事实上CPTest1同时也会被编译。编译器在正确的位置找到这个.java文件,但它不能判定这个.java文件是否包含正确的类,所以它编译这个文件。但请注意如果是:
  cd [root]
  javac -classpath "." com/web_tomorrow/CPTest1.java
  就不会导致CPTest2.java被编译,因为编译器编译CPTest1时不需要知道CPTest2的任何事情。
  类文件与.java文件分开的情况
  到目前为止所有成功的例子都把输出的.class文件放到.java文件同样的位置,这是一种比较简单的模式,应用非常广泛。但很多开发者喜欢把源文件和生成的文件分开,因此它必须告诉编译器为.class文件维护不同的目录。下面我们来看看这对类搜索路径有何影响。
  首先我们删除刚才几个例子产生的所有.class文件。我们还要有一个新的目录来存放生成的.class文件。命令行方式下的操作过程如下:
  cd [root]
  rm com/web_tomorrow/*.class
  mkdir classes
  如果你用Windows系统,别忘了把'/'替换成'\',现在的目录结构如下:
  [root] com
  web_tomorrow
  CPTest1.java
  CPTest2.java classes
  现在编译CPTest1.java,指定类文件的目标目录(通过-d选项)
  cd [root]
  javac -d classes -classpath "" com/web_tomorrow/CPTest1.java
  成功,但你会注意到,.class文件根本不是直接放在classes目录中,而是有了一个新的目录结构:
  [root] com
  web_tomorrow
  CPTest1.java
  CPTest2.java
  classes com
  web_tomorrow
  CPTest1.class

[1] [2] 下一页

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训