今天在使用命令行编译时遇到些问题,顺便又仔细分析了一些基础知识,记录总结一下。
下面使用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的一些基础问题。