今天在使用命令行编译时遇到些问题,顺便又仔细分析了一些基础知识,记录总结一下。
下面使用javac和java命令都是在 D:\Workspace\java目录下执行的:
1 //Inner.java
2 package cn.inner;
3 public class Inner
4 {
5 public static void show()
6 {
7 System.out.println("I'am inner class.");
8 }
9 }
10
11 //DoTest.java
12 import cn.inner.*;
13 public class DoTest
14 {
15 public static void main(String[] args)
16 {
17 Inner.show();
18 }
19 }
javac -d . Inner.java
javac DoTest.java
折腾半天,最终把 Inner.java 删掉、重命名都能够使 DoTest.java 编译成功。
自己思索半天,想到一个合理的解释: javac 命令在编译源文件时,如果源文件中有对其他类的调用(例如本例中的 Inner 类的调用),
它首先会在与源文件相同的目录下(即是与该类相同的包下)寻找是否有该类存在,在示例中发现了 Inner.java 源文件存在(该源文件中肯定存在一个 Inner 类),
但是事实上 Inner.java 是属于 cn.inner 包下的类,即 cn.inner.Inner 。 检查到这里, javac 命令就报错了,提示错误的源文件 ...
为了进一步证实我的假设,我修改 DoTest.java 文件,增加了默认包名:
package src;
在当前目录下执行:
Javac -d . DoTest.java
成功!
这时在分析下 DoTest.java 原源文件中导入的其它类:
由于该源文件中有导入 cn.inner.Inner 类,在使用 javac 命令编译时,该命令会按照 classpath 环境变量的路径去搜索导入的类,我的环境变量中有配置 "." ,
所以在当前目录下可以正确寻找到 cn.inner.Inner 类, 在当前目录下执行 javac DoTest.java 命令成功
但是在执行 java DoTest 命令时,出现下面的结果
这时才发现原来 DoTest.java 中增加了包名的限制,所以需要把 DoTest.class 文件移到 src 目录下,或者编译时加上 "-d ." 参数,即 javac -d . DoTest.java 命令
再执行 java src/DoTest 成功
总结: java 中包名的层级关系是用目录来体现的,比如 cn.inner 包就对应 cn\inner 这两层目录,在编译代码的过程中,如果有 import 其他包中的类,
则使用 javac 命令的命令行窗口的目录必须可以按照 classpath 环境变量的路径搜索到 import 进来的类。另外值得一提的是,编译后的类都应该按照包名规定放在指定目录下,
还需要注意被引入的类的源文件和使用引入类的源文件不能放在一起,除非两个文件都是默认包,如果是这种情况也不会使用 import 引入另外一个类了。
当然,我今天提到的这些问题,在 IDE 开发中一般都不会遇到,只是觉得这样能够更加深刻的理解 java 的一些基础问题。