java – 包查找原理

快速上手一门新语言,我个人认为很重要的一点就是:理解它是如何引入包/模块的。

这对于我们组织项目结构,引入第三方依赖都是非常重要的,相信大家都能理解。

今天讲一下JAVA是如何进行包查找的,理解它对我们学习JAVA至关重要。

编写代码

创建项目目录

首先创建空的项目目录:

创建源码目录

创建一个src目录保存项目的所有源码:

创建包目录

因为JAVA开源社区有很多很多包,每一个包里很多有class。

为了避免和开源包冲突,JAVA惯用法一般是用域名的反序作为包名,以避免冲突。

所以我创建一个包叫做:cc.yuerblog,对应目录必须是这样的:

这种存放方式是JAVA的包查找机制决定的,必须这样做。

创建包内的Class

现在cc.yuerblog这个包里面创建一个class:

这里package定义了该class所处的包名,稍后我会演示如何在其他class中引入这个Demo类,以及Java是如何基于classpath查找到这个Demo类的,让我们带着疑问继续向后。

创建另外一个包

现在创建一个包存放程序的入口文件App类,它包含main函数:

引入Demo类是按照了包名引入的,其路径相对于项目目录。

编译源码

大家写JAVA上来就用IDE,貌似IDE很简单就可以运行程序了,但其实IDE背后是做了一些幕后工作的,下面我们手动来编译程序,以便理解JAVA包的查找方式。

我们再次看一下项目结构,现在处在项目根目录mine:

源码都放在src目录下,每一个.java文件都需要编译为.class字节码文件。

我们可以先编译Demo.java,因为App.java依赖它:

.class文件默认生成在.java同目录。

接着编译App.java:

报错找不到Demo类。

怎么办呢?我们需要知道java是怎么查找包的:

JAVA是去CLASSPATH环境变量定义的各个目录下,查找包路径。

比如这里设置:CLASSPATH=/Users/liangdong/eclipse-workspace/mine/src的话,那么cc.yuerblog.Demo类就会在/Users/liangdong/eclipse-workspace/mine/src/cc/yuerblog/中查找Demo.java。

javac编译代码的时候,可以通过-cp参数指定CLASSPATH,这样javac就可以找到依赖的Demo.java文件,并将其也编译成Demo.class:

运行程序

项目所有的.class文件都编译出来之后,就可以把它们运行起来。

运行过程还是从App.class开始作为入口,后续调用到了Demo.class,因此我们运行程序得时候依旧需要classpath:

运行程序得时候,需要指定入口程序得完整类名,即:cc.yuerblog.entry.App。

因为指定了-cp ./src,所以可以在src/cc/yuerblog/entry/中找到App.class。

没有classpath则肯定无法找到./src下面的类:

一般来说我们不会通过export CLASSPATH的方式设置包查找路径,因为会影响到其他java程序得包查找路径。

创建jar包

仅仅编译出那么多.class文件还不够,我们一般会将.class打包成一个jar包,这样其他项目可以引入我们的包并访问其中的类,或者直接运行我们包的main函数拉起程序。

jar包其实就是用zip压缩的一个包含了.class文件的目录。

我们稍微改造一下javac命令,把.class文件生成到一个独立的bin目录下:

可见,.class按照包路径被放到了bin目录下。

如果我们写另外一个java类想要导入cc.yuerblog.Demo类,其实只需要在classpath中增加bin目录即可加载成功。

为了生成一个方便运输的Jar包,我们必须进入bin目录,此时相对路径才与包名一致,然后jar打包所有的.class文件:

我们可以解压一下jar文件看一下:

会发现jar包里有META-INF的元信息描述,以及所有按包名存放的class文件。

运行jar包

因为App.class具备main方法,我们可以指定jar包的入口程序,这样jar包就是可执行的了(-e参数):

现在运行jar包会从cc.yuerblog.entry.App类执行:

引用jar包

如果jar包是作为一个类库给其他人用,那么就不必指定入口类了。

假设我们新建一个项目,定义如下java类:

编译的时候,需要通过-cp指定classpath指向jar包(jar包就是一个压缩的目录),那么java自然会在jar包的cc/yuerblog/Demo.class找到类文件:

这样生成Test.class后,我们运行的时候不仅需要指定classpath包含Test.class所在目录,还需要包含jar:

javac的参数是文件名,并通过-cp指定对依赖的查找路径。

java的参数是类名,并通过-cp指定对依赖的查找路径。

java程序运行需要确保所有的.class文件都可以通过classpath找到,因此-cp参数兼具了:

  • 查找当前项目的class
  • 查找依赖的外部class(比如jar包里的,或者其他某个目录下的)

 

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~