javac和java的路径问题

javac和java的路径问题
设定项目目录为packageTest,举三个例子来讨论javac和java两个命令对路径的要求。
例1:
javac的搜索根目录为当前目录,例如,在test下建立如下目录:aaa/bbb/ccc,再在test下建立一个文件:Main.java,内容如下:
view plaincopy to clipboardprint?
1.	package aaa.bbb.ccc;  
2.	import java.io.*;  
3.	  
4.	public class Main {  
5.	    public static void main (String[] args) throws IOException {  
6.	        System.out.println("I am class Main.");  
7.	        b.say();  
8.	    }  
9.	    static B b = new B ();  
10.	}  
11.	  
12.	class B {  
13.	    public void say ()  
14.	    {  
15.	        System.out.println("I am class B.");  
16.	    }  
17.	}  
在packageTest目录下编译:
$ javac Main.java
仍在packageTest目录下运行:
$ java Main
失败。因为Main类在包aaa.bbb.ccc中,java会在CLASSPATH(假设已经将当前目录:./加入到了CLASSPATH中)下找aaa/bbb/ccc/Main.class,当然找不到。实际上,即使把CLASSPATH设为./aaa/bbb/ccc,java仍然找不到Main,这里涉及到一个fully-qualified class name的概念,即,当给一个类打了包,类的名字就不再是原来的名字,必须加上包的名字,在本例子中,Main类被打在aaa/bbb/ccc包中后,其fully-qualified class name为aaa.bbb.ccc.Main。当然,这和将类打在默认包中时的情况不同,类打在默认包中时没有fully-qualified class name,也可以说其fully-qualified class name就是原生的类名。好了,再运行:
$ java aaa.bbb.ccc.Main
仍失败!错误提示说没有找到 main class,哦,对,原来是没把Main拷到aaa/bbb/ccc中去,拷进去后再运行:
$ java aaa.bbb.ccc.Main
仍失败!!错误提示说没有找到 main class,不知道为什么还会提示这个错误,其实错误原因是B.class放的位置不对由于class B和Main放在同一个翻译单元中,故编译器图方便直接把B.class放在了当前目录中,实际上class B也是aaa.bbb.ccc包中的成员,所以运行时java没有在这个包中找到class B,当把B.class拷到aaa/bbb/ccc目录下后再运行:
$ java aaa.bbb.ccc.Main
成功显示结果:
I am class Main.
I am class B.
例2:
对Main.java做修改,把要使用的类B移出当前翻译单元:
view plaincopy to clipboardprint?
1.	package aaa.bbb.ccc;  
2.	import java.io.*;  
3.	  
4.	public class Main {  
5.	    public static void main (String[] args) throws IOException {  
6.	        System.out.println("I am class Main.");  
7.	        b.say();  
8.	    }  
9.	    static B b = new B ();  
10.	}  
然后在packageTest下新建一个B.java:
view plaincopy to clipboardprint?
1.	package aaa.bbb.ccc;  
2.	import java.io.*;  
3.	public class B {  
4.	    void say () {  
5.	        System.out.println("I am the goddamn B!");  
6.	    }  
7.	}  
编译:
$ javac Main.java
失败。原因是javac没有找到symbol: class B,把B.java拷到aaa/bbb/ccc下后,编译成功,并在aaa/bbb/ccc下生成了B.class。原来javac在本翻译单元中找不到B后会去包aaa.bbb.ccc中找,所以package语句不止跟java有关,javac也要用它。实际上,javac需要的也是class文件,即javac发现import语句后,也要去CLASSPATH中去找编译时所需要的类,如果class文件存在,直接使用;如果不存在,对应的java源文件存在也行,这时,javac会先将B.java编译成B.class,这样Main中所需要的aaa.bbb.ccc.B就存在了,编译通过。因此,不论是java还是javac还是其它程序,只要其找的是class文件,就需要到CLASSPATH指定的目录中去找。如果CLASSPATH不设,默认为当前目录".",故即使echo出的$CLASSPATH什么也没有,它也是".",这一点比较奇怪。另外,目前还不清楚java的“库“放在哪,比如上面程序中的java.io.*,而且java怎么去找这些class,至少应该不是通过CLASSPATH。javac编译过程中寻找类名的顺序是定死的,这个顺序参见附记。
接下来在test中运行:
$ java aaa.bbb.ccc.Main
失败。忘记把Main.class拷到aaa/bbb/ccc中去了。拷过去以后在test中执行上面命令,结果为:
I am class Main.
I am the goddamn B!
例3:
主要想明确一下javac搜索类型的顺序。
Main.java如例1:
view plaincopy to clipboardprint?
1.	package aaa.bbb.ccc;  
2.	import java.io.*;  
3.	  
4.	public class Main {  
5.	    public static void main (String[] args) throws IOException {  
6.	        System.out.println("I am class Main.");  
7.	        b.say();  
8.	    }  
9.	    static B b = new B ();  
10.	}  
11.	  
12.	class B {  
13.	    public void say ()  
14.	    {  
15.	        System.out.println("I am class B.");  
16.	    }  
17.	}  
另外还有B.java:
view plaincopy to clipboardprint?
1.	package aaa.bbb.ccc;  
2.	import java.io.*;  
3.	public class B {  
4.	    void say () {  
5.	        System.out.println("I am the goddamn B!");  
6.	    }  
7.	}  
这次两个class都放在aaa/bbb/ccc下,在packageTest中执行命令:
$ javac aaa/bbb/ccc/Main.java
在aaa/bbb/ccc下生成Main.class和B.class,然后运行:
$ java aaa.bbb.ccc.Main
结果为:
I am class Main.
I am class B.
可见生成的B.class由Main.java中的class B生成,这体现了javac的一种搜索类型的顺序。
 
javac参数(-classpath, -sourcepath)详解
首先是官方说法:
-classpath:
设置用户类路径,它将覆盖 CLASSPATH 环境变量中的用户类路径。若既未指定 CLASSPATH 又未指定 -classpath,则用户类路径由当前目录构成。
 
-sourcepath:
sourcepath用来查找源文件的,具体来说就是,在我们编译A时,如果A依赖B,而B在classpath中又找不到,这时就要在sourcepath中查找B的源文件。指定用以查找类或接口定义的源代码路径。与用户类路径一样,源路径项用分号 (;) 进行分隔,它们可以是目录、JAR 归档文件或 ZIP 归档文件。如果使用包,那么目录或归档文件中的本地路径名必须反映包名。
注意:通过类路径查找的类,如果找到了其源文件,则可能会自动被重新编译。
 
    官方说法很简洁,但是具体操作起来总觉得不太清晰,我们还是找几个例子,来具体分析一下:
具体例子:
    假设我们有如下的目录结构:
    src-|
          |-foo-|
                   |-Testfoo.java
          |-baz-|
                    |-Testbaz.java
    classes-|
其中Testfoo.java:
package foo;
public class Testfoo {
//....
}
其中Testbaz.java:
package baz;
import foo.Testfoo;
public class Testbaz {
//...
}
也就是说Testbaz对Testfoo有引用。
我们目标是把Testbaz编译到目录classes里面,试用如下的命令:
javac -d classes src/baz/Testbaz.java
命令失败,因为找不到它依赖的Testfoo。我们可以用以下三种方式来实现:
1.我们最容易考虑到的就是编译被依赖的Testfoo类,并将其加入Testbaz的CLASSPATH
javac -d classes src/foo/Testfoo.java
javac -d classes -classpath classes src/baz/Testbaz.java
第二行使用了-classpath,使得编译器在寻找Testfoo类的时候以classes为根目录,根据根目录和package名,类名最终定位了需要用的(已编译出来的)Testfoo类。
 
2.在编译Testbaz的时候把被依赖的Testfoo类加入SOURCEPATH
javac -d classes -sourcepath src src/baz/Testbaz.java
在编译条件里面加入-verbose可以很清楚的看到,编译器在寻找Testfoo类的时候,以src为根目录,根据根目录和package名,类名最终定位了需要用的(源代码)Testfoo类。
 
3.把CLASSPATH和SOURCEPATH两者都加进来
javac -d classes src/foo/Testfoo.java
javac -d classes -sourcepath src -classpath classes src/baz/Testbaz.java
两个属性都被加入,编译器首先会判断Testfoo.class和Testfoo.java是否同时存在。如果单独存在,则适应于以上的方法之一。如果同时存在,则判断.class是否是.java的最新编译,如果有差异,则重新编译.java来覆盖.class
 
有必要补充一下-d参数:
-d参数是很好的把源程序和目标代码分离的参数。-d制定的是目标代码的根目录,源文件的包的结构将以目录形式反映到根目录上。美中不足的是-d需要指定已经存在的目录,不能自动创建。
再来一例子
文件结构如下,其中A.java头部引用B和C
 
files.txt为sourcepath列表,注意路径以分号分隔且在同一行(不知为什么,可能与classpath规范类似,路径以分号分隔吧),不能以回车或者空格分隔,另外路径指类全名(含包名)所在的目录,如A .java中import c.C;指c.C所在的目录即E:\test\src,而不是C.java的路径即E:\test\src\c。
 
javas.txt包含多个要编译的java源文件,以空格或者回车分隔,分号不行。也可以将多个文件写在javac命令行中,文件名以空格分隔。要编译的源文件要写成含”\”符号的完整路径,不能写成含”.”符号的包形式,这与java命令不同,如javac E:\test\b\B.java与java b.B。
 
E:\test\a\A.java
E:\test\b\B.java
E:\test\src\c\C.java
 
 
 
 
 
 

 

你可能感兴趣的:(面试,Java疑难)