乐趣区

没了IDE你的Java项目还能Run起来吗~

计算机只能识别机器码 0101… 编程语言 -> 能执行的机器码 需要经过 预处理 -> 编译 -> 汇编 -> 链接 -> 机器码 过程。一个语言处理系统的示意图如下:

编译器 是将 源语言程序一次性翻译 成一个等价的,用目标语言编写的程序。还存在另一种常见的语言处理器,解释器 :它是 逐个语句的执行源语言程序。由一个编译器产生的目标语言程序通常比一个解释器快,但解释器的错误诊断效果通常更好。

Java 语言处理器结合了编译和解释的过程。一个 .Java 源程序首先被编译为 .class 字节码文件,被加载到虚拟机中,然后由虚拟机将字节码翻译成机器码。

虚拟机的好处 在于:一旦一个程序被转换成 Java 字节码,那么它便可以在不同平台上的虚拟机实现里运行。实现 一次编写,到处运行 。另外一个好处是它带来了一个 托管环境。这个托管环境能够代替我们处理一些代码中冗长而且容易出错的部分,如自动内存管理与垃圾回收。

在 Hotspot 中,虚拟机翻译字节码 有两种方式:

1. 解释执行
即逐条将字节码翻译成机器码并执行。

2. 即时编译
即将一个方法中包含的所有字节码编译成机器码后再执行。

前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。HotSpot 默认采用混合模式,综合了解释执行和即时编译两者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。

即时编译建立在程序符合二八定律的假设上,也就是百分之二十的代码占据了百分之八十的计算资源。

好了,装 X 结束。

阿姨知道的编译知识全在上面了。。(っ╥╯﹏╰╥c)

如题,下面我们来看一下让 Java 项目运行起来我们能做什么。

我们能做的很简单,当然不是写虚拟机。我们只需要:

1. 执行command javac,将.Java 文件变为.class 文件。
2. 执行command java,让.class 文件运行起来。

也就是 执行 command:)

Java 程序的运行方式

Java 程序可以通过 java 命令 运行.class 文件 运行可执行 Jar 文件
我们先看第一种方式:从 Hello World 开始。

运行.class 文件

Step1: 编写 Java 文件

Step2: 执行 command javac

将.Java 文件变为.class 文件

小贴士:class 文件的全路径名是包名目录 + 类文件名。

Step3: 执行 command java

运行.class 文件

神奇,我们没有用 IDE 让 Java 程序运行起来了:)

小伙伴先别喷老阿姨,哪特么有这么简单的 Java 项目啊。。我们工作中用的明明都是 Jar 文件啊 …
Jar 文件咋运行啊!!

运行可执行 Jar 文件

Jar 文件 是基于 ZIP 文件格式的一种文件格式,它将大量的 Java 类文件、相关的元数据和资源(文本、图片等)文件聚合到一个 Jar 文件中,此外还包含一个可选的 META-INF 文件夹。这个文件夹下的文件或文件夹主要用来打包和扩展配置信息,包括安全,版本,扩展程序和服务等。如MANIFEST.MF 文件定义了扩展和打包的相关数据信息。
一个 Jar 文件通常在项目中用作第三方类库使用,也是项目构建的一部分。

生成一个 Jar 文件大致分为两步:

1. 将源文件编译为.class 文件

2. 通过 command jar命令将.class 文件,资源文件等等打成一个文件格式的 Jar 文件。

我们以一个 SbDemo 项目为例来看 Jar 文件的打包和运行。项目目录结构如下:

Test2.java 中调用了 Test1.java 的方法,

我们需要先 将 Test1.java 编译并打成一个 Test1.jar 文件,然后通过 Test1.jar 将 Test2.java 编译并打成一个可执行的 Test2.jar 文件

可执行和不可执行的 Jar 文件 区别在于是否在 Jar 文件中指定了 main 方法的入口,我们后面再看。

Step1:Test1.java 的编译

Step2: 将编译后的 classes/com/Test1.class 文件打成一个 Test1.jar 包

Java 中和 jar 包相关的命令是 jar 命令,生成一个 jar 包我们需要定义 信息文件 (manifest-file),它可以定义所生成 jar 包的 classpath 类搜索路径,jar 包的入口类等等。可以理解为 与 Jar 包相关的元数据配置信息
Step2.1 书写信息文件
这里我们使用 resources/manifest-test1.text 文件 作为信息文件

是的,Test1.java 太简单了,就是打成一个可被他人引用的 jar 包,信息文件不重要。
Step2.2 执行打包命令

Step3. 编译 Test2.java 文件
因为 Test2.java 中引用了 com.Test1 类,所以我们需要在编译时指定 Classpath 路径。
Classpath:顾名思义,是指待编译类依赖的类所在路径位置。我们可以通过 javac 的 -cp 参数指定。
关于编译时 classpath 的值优先级如下:

  • 如果没有传入 classpath 参数,将使用环境变量 CLASSPATH 的值。(小伙伴不知道环境变量咋查看和设置?去看阿姨的上一篇文章:)
  • 如果没有发现环境变量 CLASSPATH,将使用 执行命令的当前文件夹(.)。
  • 如果 javac 命令行 通过 -classpath or -cp 参数指定了类路径值,则优先级最高。

这里我们使用 -cp 指定 Test1.jar 所在位置

可以看到 classes 目录下已经生成了 com2/Test2.class 文件了。

Step4. 将编译后的 Test2.class 和它依赖的 Test1.jar 一起打成一个可执行的 Jar 包
Step4.1 书写信息文件
这时候我们使用信息文件 resources/manifest-test2.text 文件指定这些信息

Step4.2 执行 Jar 包生成命令

可以看到在 lib 目录下生成了 Test2.jar

Step5. 运行我们的可执行 Jar

大功告成了,我们的 SbDemo 项目 Run 起来了 …

当然实际项目不可能人肉编译,打包。我们需要通过 Maven/Gradle 等构建工具,帮助我们管理代码之间的 Jar 包依赖,构建,部署 … 我们可能大多时候通过点一下 IDE 就托管了 Maven 的构建部署命令。

拿 Maven 举例子,Maven 首先定义了一套项目结构,我们按照它的结构书写代码,引入各个模块所需要的 Jar 包依赖。然后 Maven 可以通过自己的生命周期管理项目的清理,构建,打包,部署阶段。每个阶段有对应的 Maven 插件执行相应的目标。IDE 又整合了 Maven,使我们通过点吧点吧按钮就完成了项目的运行。

但是当一个项目并没有按照规范的构建工具结构搭建,或者项目没有成功运行报错时,了解 Java 实际的编译运行过程会对理解、解决这类问题有所帮助。

好啦,限于篇幅,阿姨先不讲这些年 Maven 躺过的坑了,有想看的吗?关注,在看,转发 三连回应下 >-<


参考资料:
[1].《编译原理》序 (゚´ω`゚)゚
[2].https://time.geekbang.org/column/article/11289

退出移动版