编译
导航 入门 主题: ) |
在 Java 中,程序不会编译成可执行文件;它们被编译成字节码(如之前所述),然后 JVM(Java 虚拟机)在运行时执行它。当我们使用 javac
编译器时,Java 源代码被编译成字节码。字节码以 .class
文件扩展名保存到磁盘上。当程序要运行时,字节码会使用即时 (JIT) 编译器进行转换。结果是机器码,然后被送入内存并执行。
Java 代码需要编译两次才能执行
- Java 程序需要编译成字节码。
- 当字节码运行时,它需要被转换成机器码。
Java 类/字节码在第一次需要时被 JVM 编译成机器码并加载到内存中。这与 C/C++ 等其他语言不同,在这些语言中,程序需要编译成机器码并链接以创建可执行文件,然后才能执行。
要执行您的第一个 Java 程序,请按照以下说明进行操作
1. | 如果您已成功安装并配置好系统以使用 Java,如这里所述,则继续执行。 | ||||
2. | 打开您首选的文本编辑器 - 这是您在安装 Java 平台时设置的编辑器。 | ||||
例如,在 Windows 上使用 记事本 或 Notepad++;在 Linux 上使用 Gedit、Kate 或 SciTE;或者,在 Mac OS 上使用 XCode 等。 | |||||
3. | 在新的文本文档中编写以下代码行
| ||||
4. | 将文件保存为 HelloWorld.java - 您的文件名应与您的类定义的名称相同,并在后面加上 .java 扩展名。此名称是区分大小写的,这意味着您需要将类定义名称中的字母大小写与您保存的文件名大小写保持一致。 | ||||
5. | 接下来,打开您首选的命令行应用程序。 | ||||
例如,在 Windows 上使用 命令提示符;在 Linux 和 Mac OS 上使用 终端。 | |||||
6. | 在命令行应用程序中,导航到您刚刚创建文件的目录。如果您不知道如何执行此操作,请考虑阅读我们的命令行应用程序速成课程,适用于Windows 或 Linux。 | ||||
7. | 使用以下命令编译 Java 源文件,如果您需要,您可以复制粘贴。
| ||||
8. | 一旦编译器返回到提示符,请使用以下命令运行应用程序
| ||||
9. | 上面的命令应导致您的命令行应用程序显示以下结果
|
- 如果程序没有正确执行,请在本章的讨论页面中寻求帮助.
在 Java 中,如果您使用了对任何其他 java 对象的引用,那么该对象的类将被自动编译,如果它还没有被编译的话。这些自动编译是嵌套的,并且会一直持续到所有运行程序所需的类都被编译完成。因此,通常只编译高级类就足够了,因为所有依赖类都会被自动编译。
主类编译
javac ... MainClass.java |
但是,如果您程序使用反射来创建对象,或者您正在为 servlet 或“jar”包编译,则不能依赖此功能。在这些情况下,您应该列出这些类以进行显式编译。
主类编译
javac ... MainClass.java ServletOne.java ... |
每个 Java 顶级类都属于一个包(在关于包的章节中介绍)。这可以在文件开头的package
语句中声明;如果缺少该语句,则该类属于未命名包。
为了编译,文件必须位于正确的目录结构中。包含未命名包中类的文件必须位于当前/根目录中;如果类属于某个包,则它必须位于与包同名的目录中。
惯例是,与包相对应的包名和目录名仅包含小写字母。
具有此包声明的类
代码节 2.1:包声明
package example;
|
必须位于名为example
的目录中。
具有此包声明的类
代码节 2.2:带子包的包声明
package org.wikibooks.en;
|
必须位于名为en
的目录中,该目录必须是wikibooks
的子目录,而wikibooks
又必须是org
的子目录,最终形成Linux上的org/wikibooks/en
或Windows上的org\wikibooks\en
。
Java 程序通常包含非代码文件,例如图像和属性文件。这些通常称为资源,并存储在它们所用类的本地目录中。例如,如果类com.example.ExampleApp
使用icon.png
文件,则该文件可以存储为/com/example/resources/icon.png
。当程序编译时,这些资源会带来问题,因为javac
不会将它们复制到.class
文件正在编译到的位置(见上文);程序员需要自己移动资源文件和目录。
Java 源文件名必须与文件包含的公共类名相同。每个文件只能定义一个公共类。Java 类名和源文件名都区分大小写。
类名命名规范是首字母大写。
要调试到 Java 系统类(如 String 和 ArrayList),您需要使用编译了“调试信息”的 JRE 特殊版本。JDK 中包含的 JRE 提供了此信息,但常规 JRE 则没有。常规 JRE 不包含此信息,以确保更好的性能。
Modern compilers do a pretty good job converting your high-level code, with its nicely indented and nested control structures and arbitrarily typed variables into a big pile of bits called machine code (or bytecode in case of Java), the sole purpose of which is to run as fast as possible on the target CPU (virtual CPU of your JVM). Java code gets converted into several machine code instructions. Variables are shoved all over the place – into the stack, into registers, or completely optimized away. Structures and objects don’t even exist in the resulting code – they’re merely an abstraction that gets translated to hard-coded offsets into memory buffers.
So how does a debugger know where to stop when you ask it to break at the entry to some function? How does it manage to find what to show you when you ask it for the value of a variable? The answer is – debugging information.
Debugging information is generated by the compiler together with the machine code. It is a representation of the relationship between the executable program and the original source code. This information is encoded into a pre-defined format and stored alongside the machine code. Many such formats were invented over the years for different platforms and executable files.
符号信息:符号解析在类加载时在链接解析步骤中完成。它是用直接引用替换类型中的符号引用的过程。它是通过在方法区中搜索以定位所引用实体来完成的。
即时 (JIT) 编译器是将字节码转换为机器码的编译器。它一次编译字节码,并且编译后的机器码会反复使用,以加快执行速度。早期的 Java 编译器每次使用字节码时都会将其编译为机器码,但更现代的编译器会缓存此机器码以在机器上重复使用。即使如此,Java 的 JIT 编译仍然比“解释语言”更快,在解释语言中,代码每次使用时都从高级语言而不是从字节码编译。
标准 JIT 编译器按需运行。当方法被反复调用时,JIT 编译器会分析字节码并生成高效的机器码,运行速度非常快。JIT 编译器足够智能,可以识别代码是否已编译,因此,随着应用程序的运行,编译只会根据需要发生。随着 Java 应用程序的运行,它们往往会变得越来越快,因为 JIT 可以对代码进行运行时分析和优化以满足执行环境。运行不频繁的方法或代码块接收的优化较少;运行频繁的方法或代码块(所谓热点)接收的分析和优化更多。