先编译,再运行
装好 JDK 之后,运行 Java 就是一个两步的流程。脚本语言只要把解释器指向一个文件就能运行,Java 则不同:它先把人类可读的源代码编译成字节码,然后由单独的一步在 JVM 上运行这些字节码。
这两步对应着 JDK 自带的两个命令行工具:
javac:编译器。它读取Main.java,写出Main.class(字节码)。java:启动器。它启动 JVM 并执行Main.class中的字节码。
任何独立的示例都可以直接在本页运行,下面的编辑器会替你完成这两步。不过,弄清楚在你自己的电脑上发生了什么还是很有价值的,因为真正的项目都在那里。
在你电脑上的两条命令
假设你把这段代码保存为 Main.java:
public class Main {
public static void main(String[] args) {
System.out.println("Hello from the terminal");
}
}
在存放该文件的文件夹中打开终端,运行编译器:
javac Main.java
如果代码编译顺利,javac 什么都不会打印,而是在源文件旁边生成一个新文件 Main.class。这个 .class 文件就是字节码:它不是可读的文本,也不依赖于你的操作系统。
现在用 java 命令启动它:
java Main
Hello from the terminal
初学者最常犯的错误就在这里:启动程序用的是 java,而不是 javac,并且要传入不带扩展名的类名。写 java Main,永远不要写 java Main.class,也不要写 java Main.java。
文件名必须与公共类同名
Java 有一条让新手意外的规则:public 类必须放在名字完全相同、再加上 .java 的文件里。public class Main 必须在 Main.java 中。大小写写错了,javac 就会拒绝编译。
// This is in a file called Greeting.java
public class Greeting { // ERROR: class Greeting should be in Greeting.java... wait, it is
public static void main(String[] args) {
System.out.println("Hi");
}
}
真正让人栽跟头的不匹配要隐蔽得多:类名是 Greeting,文件却命名为 greeting.java(小写);或者保存为 Main.java,里面却写的是 public class Hello。编译器会报出类似这样的错误:
Main.java:1: error: class Hello is public, should be declared in a file named Hello.java
解决方法永远一样:让文件名和公共类名一模一样,一个字符都不差。
捷径:直接运行单个文件
从 JDK 11 开始,单个源文件可以跳过显式的编译步骤。java 命令会在内存中编译它并一次性运行,不会往磁盘写入 .class 文件:
java Main.java
这非常适合快速试验和小脚本。注意传入内容的区别:
java Main:运行一个已经编译好的Main.class。java Main.java:一步完成源文件的编译和运行。
单文件模式只在整个程序都放在一个文件里时才有效。一旦你把代码拆分到多个文件中的多个类里,就要回到先用 javac 编译的方式。学习基础知识时,单文件模式能让“修改、运行”的循环更短。
给程序传递参数
还记得 main 里的 String[] args 吗?那就是命令行参数。在 java 命令中类名之后输入的任何内容,都会进入这个数组:
在你自己的电脑上,可以这样给它传入参数:
java Main hello world
First argument: hello
Total arguments: 2
上面的编辑器无法接收终端参数,所以它运行的是“没有参数”的分支,但同一段代码能处理两种情况。这就是 Java 程序读取启动时传入的输入的方式,远在你接触文件或用户输入之前。
读懂编译器的错误信息
当 javac 拒绝你的代码时,它会告诉你文件、行号以及出了什么问题。学会读这些信息,就解决了一半的卡壳问题。下面是一个经典例子,漏了分号:
public class Main {
public static void main(String[] args) {
System.out.println("Oops") // no semicolon
}
}
Main.java:3: error: ';' expected
System.out.println("Oops")
^
1 error
插入符号(^)指向编译器期望出现某样东西的位置。Main.java:3 是文件名和行号。不要急着瞎猜:读一读那一行,修复它指出的那一个问题,然后重新编译。编译错误意味着还什么都没运行;运行时错误(看起来不一样)则意味着程序已经启动,然后失败了。
一个自检程序
在编辑器里运行它,或者把它保存为 Main.java,在你自己的电脑上先执行 javac Main.java 再执行 java Main。如果三行都打印出来了,说明你的工具链从头到尾都能正常工作:
这里出现了三样你很快就会正式学到的东西:一个 int 变量、用 + 拼接字符串,以及一个数组和它的 .length。现在只要程序能编译并打印出来就够了。
下一页:Java 语法
你已经运行了几个程序,但我们一直没细说那些符号:花括号、分号、public static void main,以及为什么每一行都长成这样。下一页会逐一拆解 Java 的语法,让这些结构不再像是照抄的模板,而是真正说得通。
常见问题
如何运行 Java 程序?
把代码保存在以公共类命名的文件中(例如 Main.java),在该文件夹中打开终端,然后运行两条命令:javac Main.java 把它编译成字节码,再用 java Main 运行它。使用 JDK 11 或更新版本时,单个文件可以跳过第一步,直接运行 java Main.java。
javac 和 java 有什么区别?
javac 是编译器:它读取你的 .java 源代码,生成一个装满字节码的 .class 文件。java 是启动器:它启动 JVM 并运行 .class 文件中的字节码。用 javac 编译一次,之后用 java 想运行多少次都可以。
为什么会出现“找不到或无法加载主类”(could not find or load main class)?
几乎都是因为你传给 java 的名字不对。要用类名,而不是文件名:写 java Main,而不是 java Main.class 或 java Main.java。还要确认你所在的文件夹里有 Main.class,并且类名与文件名完全一致,包括大小写。