JVM系列:(三)编译

日期:2019-01-20       浏览:481

代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步

一 什么是编译

编译是将一种抽象程度更高的语言转换为更贴近计算机能够识别的语言的过程。
在Java中存在两次编译,不过我们今天说的是其中的第一次编译,就是将 .java 源程序编译为 .class 字节码文件的过程。

二 为什么要编译

2.1 高级语言

在说为什么要编译之前我们先说一下什么是高级语言。
高级语言是相对的,如果比较的对象是最底层的机器码的话,C、C++、Java 等都是高级语言,如果比较的对象是C、C++ 的话,Java就是高级语言。
下面我们就拿C、C++ 和 Java 对比一下,为什么说 Java是高级语言,这里所说的高级具体指的是什么?
  • 语法层面上:相对于机器码、汇编语言等,Java语言更贴近人类的自然语言,容易理解和使用。
  • 跨平台能力上:相对于C语言、C++等,Java语言做到了平台无关性,不需要去做不同操作系统的兼容。
  • 内存管理上:相对于C语言、C++等,Java开发者不需要管理内存,可以投入更多的精力关注和实现具体的业务。

2.2 Java语言慢?

上面说了Java具有这么多的优点,那为什么还有人去用C、C++ 做开发语言呢?用Java岂不是事半功倍?说到这里我们先说一下不知道你有没有听过这么个说法,“Java语言慢”。当然这个说法是很久以前的了,现在的Java虚拟机进行过很多优化,运行速度可不比 C++ 慢多少了。
为什么说“Java语言慢”?Java开发者都知道,Java源程序要想运行必须要经过以下步骤:
Java源程序 -> 编译为.class字节码 -> JVM编译为机器码 -> 操作系统运行
那有人又会说了,C语言要想运行也是需要编译的。那我们来看一下C语言运行需要经过哪些步骤:
C源程序 -> 编译为机器码 -> 操作系统运行
到这里应该就有人发现了,Java是要经过两次编译的,第一次编译的结果.class字节码文件,这个文件是给 JVM 识别的。到这里可能又有人会问了,Java为什么不直接编译为机器码,那样不就省去了中间编译的过程,并且运行效率也会提高吗?答案是在Java运行平台JVM内存在的二次即时编译器编译的时候,因为有程序的运行时信息,编译为机器码后的优化效果更好,也就是执行性能更高(即时编译的相关信息我们会在后面章节详细讲解)。
JVM是Java的运行平台,但这个平台说是给Java运行的,更具体点说应该是 .class 字节码文件运行的平台,因为JVM定义了一套.class字节码文件格式规范,只要符合这个规范的.class文件都可以在JVM平台上运行。

2.3 JVM多语言平台

上面说了JVM只识别符合规范定义的.class字节码文件,那是不是说我自定义一门开发语言,然后编译为符合JVM规范定义的.class字节码,就可以像Java一样在JVM上运行了呢?答案是是的!
大家应该对Groovy、Scala等编程都有所耳闻甚至有的同学都使用过。这两门语言就和Java一样,需要经过编译为符合JVM规范定义的.class字节码文件,然后就可以在JVM平台上运行了,如下图所示。
JVM的多语言平台
JVM的多语言平台
JVM的这种规范定义促使了很多可在JVM平台上运行的新的编程语言的出现,每门语言都有自己擅长的领域,通过特定语言去解决特定领域的问题是当前软件开发应对日趋复杂的项目需求的一个方向。

三 Java的编译运行

安装Java运行环境JDK的时候,就自带了Java编译工具javac以及运行工具java。下面我们就来使用一下这两个工具。

3.1 编译及运行相关命令

编译 .java 文件的命令 javac 用法:
用法: javac <options> <source files>
其中, 可能的选项包括:
  -g                         生成所有调试信息
  -g:none                    不生成任何调试信息
  -g:{lines,vars,source}     只生成某些调试信息
  -nowarn                    不生成任何警告
  -verbose                   输出有关编译器正在执行的操作的消息
  -deprecation               输出使用已过时的 API 的源位置
  -classpath <路径>            指定查找用户类文件和注释处理程序的位置
  -cp <路径>                   指定查找用户类文件和注释处理程序的位置
  -sourcepath <路径>           指定查找输入源文件的位置
  -bootclasspath <路径>        覆盖引导类文件的位置
  -extdirs <目录>              覆盖所安装扩展的位置
  -endorseddirs <目录>         覆盖签名的标准路径的位置
  -proc:{none,only}          控制是否执行注释处理和/或编译。
  -processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
  -processorpath <路径>        指定查找注释处理程序的位置
  -parameters                生成元数据以用于方法参数的反射
  -d <目录>                    指定放置生成的类文件的位置
  -s <目录>                    指定放置生成的源文件的位置
  -h <目录>                    指定放置生成的本机标头文件的位置
  -implicit:{none,class}     指定是否为隐式引用文件生成类文件
  -encoding <编码>             指定源文件使用的字符编码
  -source <发行版>              提供与指定发行版的源兼容性
  -target <发行版>              生成特定 VM 版本的类文件
  -profile <配置文件>            请确保使用的 API 在指定的配置文件中可用
  -version                   版本信息
  -help                      输出标准选项的提要
  -A关键字[=值]                  传递给注释处理程序的选项
  -X                         输出非标准选项的提要
  -J<标记>                     直接将 <标记> 传递给运行时系统
  -Werror                    出现警告时终止编译
  @<文件名>                     从文件读取选项和文件名
运行编译结果 .class 文件的命令 java 用法:
用法: java [-options] class [args...]
           (执行类)
   或  java [-options] -jar jarfile [args...]
           (执行 jar 文件)
其中选项包括:
    -d32      使用 32 位数据模型 (如果可用)
    -d64      使用 64 位数据模型 (如果可用)
    -server      选择 "server" VM
                  默认 VM 是 server,
                  因为您是在服务器类计算机上运行。


    -cp <目录和 zip/jar 文件的类搜索路径>
    -classpath <目录和 zip/jar 文件的类搜索路径>
                  用 : 分隔的目录, JAR 档案
                  和 ZIP 档案列表, 用于搜索类文件。
    -D<名称>=<值>
                  设置系统属性
    -verbose:[class|gc|jni]
                  启用详细输出
    -version      输出产品版本并退出
    -version:<值>
                  警告: 此功能已过时, 将在
                  未来发行版中删除。
                  需要指定的版本才能运行
    -showversion  输出产品版本并继续
    -jre-restrict-search | -no-jre-restrict-search
                  警告: 此功能已过时, 将在
                  未来发行版中删除。
                  在版本搜索中包括/排除用户专用 JRE
    -? -help      输出此帮助消息
    -X            输出非标准选项的帮助
    -ea[:<packagename>...|:<classname>]
    -enableassertions[:<packagename>...|:<classname>]
                  按指定的粒度启用断言
    -da[:<packagename>...|:<classname>]
    -disableassertions[:<packagename>...|:<classname>]
                  禁用具有指定粒度的断言
    -esa | -enablesystemassertions
                  启用系统断言
    -dsa | -disablesystemassertions
                  禁用系统断言
    -agentlib:<libname>[=<选项>]
                  加载本机代理库 <libname>, 例如 -agentlib:hprof
                  另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help
    -agentpath:<pathname>[=<选项>]
                  按完整路径名加载本机代理库
    -javaagent:<jarpath>[=<选项>]
                  加载 Java 编程语言代理, 请参阅 java.lang.instrument
    -splash:<imagepath>
                  使用指定的图像显示启动屏幕
有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation/index.html。

3.2 编写测试类

/**
 * 注意这里无任何包名
 */
public class TestClass {

    public static void main(String[] args) {
        System.out.println("hello java!");
    }

}
保存文件名 TestClass.java。

3.3 编译 .java 文件

// cd 到 TestClass.java 文件所在目录
cd /xxx/xxx

// 使用 javac 命令对 TestClass.java 文件进行编译
javac TestClass.java

// 编译成功后会在当前目录下生成一个 TestClass.class 字节码文件

3.4 运行 .class 文件

// cd 到 TestClass.class 文件所在目录
cd /xxx/xxx

// 使用 java 命令运行 TestClass.class 字节码文件
// 注意这里运行的文件无后缀名,默认运行的是 .class 文件
java TestClass

// 运行成功后控制台会输出 hello java!

四 总结

以上就是我们要说的编译相关内容,提到了:什么是编译、什么是高级语言、JVM为什么是多语言平台、怎么编译Java源码并运行等。
接下来我们就说一下Java项目上线前要做的最后一件事,打包。
扫码关注有惊喜

(转载本站文章请注明作者和出处 qbian)

暂无评论

Copyright 2016 qbian. All Rights Reserved.

文章目录