跳到内容

第 1 卷 / 第 1 章 / 第 5 课

1.5 修错、改输出、验证入口 ​

前置知识:本章前四讲

预计时间:30 分钟

练习环境:JDK 17+ 与 Bash;所有文件写入临时目录

完成标志:完成三项必做实验,并保存命令和结果

所有练习都在新目录中进行:

bash
chapter1_lab_root=$(mktemp -d)
readonly chapter1_lab_root
mkdir -p "$chapter1_lab_root/chapter01-lab/out"
cd "$chapter1_lab_root/chapter01-lab"

删除或重建产物时,只操作这里的 out。

必做一:修复三类编译错误 ​

保存为 Bug.java:

java
public class Bug {
    public static void main(String[] args) {
        system.out.println("Hello, Java!")
        System.Out.println("Hello Coder!");
    }
}

先原样编译:

bash
javac -d out Bug.java

把第一条诊断记录下来,然后依次修复:

  1. 第一条调用末尾的英文分号;
  2. system 的大小写;
  3. Out 的大小写。

每改一处就重新编译。最终运行:

bash
java -cp out Bug

预期输出:

text
Hello, Java!
Hello Coder!

注意:一处语法错误可能引发多条连锁诊断,因此初始错误数量未必正好等于源码中的独立根因数量。

必做二:观察重新编译 ​

保存为 Greeting.java:

java
public class Greeting {
    public static void main(String[] args) {
        System.out.println("version one");
    }
}
bash
javac -d out Greeting.java
java -cp out Greeting

把源码改为 version two,但先不要编译,直接运行一次。你应仍看到 version one。随后重新编译再运行,输出才会变成 version two。

在实验记录中写一句解释:java 运行的是哪一个文件?

必做三:使用命令行参数 ​

保存为 Greeter.java:

java
public class Greeter {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Hello, user!");
            return;
        }
        System.out.println("Hello, " + args[0] + "!");
    }
}

编译后运行两次:

bash
javac -d out Greeter.java
java -cp out Greeter
java -cp out Greeter Lin

预期输出:

text
Hello, user!
Hello, Lin!

你还没系统学习 if 和数组,当前只需确认:启动命令中类名之后的词会进入 args。

选做一:print、println 与转义序列 ​

java
public class Lines {
    public static void main(String[] args) {
        System.out.print("north ");
        System.out.println("gate");
        System.out.println("line 1\nline 2");
        System.out.println("A\tB");
    }
}

在运行前先写下你预测的四行结果,再编译验证。终端对制表符的显示宽度可能不同,所以只检查 A 与 B 之间存在制表间隔。

选做二:入口方法可以重载吗 ​

保存为 EntryPoints.java:

java
public class EntryPoints {
    public static void main(String[] args) {
        System.out.println("launcher entry");
        main(3);
    }

    public static void main(int count) {
        System.out.println("overload: " + count);
    }
}

它可以编译,因为两个方法参数类型不同,属于重载。Java 启动器只选择字符串数组形式的传统入口,main(int) 是程序自己调用的普通重载。

现在复制一份 main(String[] args),保持签名完全相同。编译器会拒绝同一类中的重复方法。把“同名”和“同签名”区分开。

选做三:清理产物后再验证 ​

bash
pwd
test ! -e out || rm -r -- out
mkdir out
javac -d out Greeter.java
java -cp out Greeter Ada

这能证明结果不依赖旧产物。pwd 必须显示当前实验目录;不要在不确定位置时运行递归删除。

实验记录模板 ​

实验原始命令第一条关键诊断或输出修改复验结果
Bug
重新编译
参数

验收与清理 ​

确认 Bug.java 得到两行输出,亲自观察过旧字节码仍会运行,并让 Greeter 的无参数、有参数两条路径都通过。实验记录保留到需要的位置后,删除临时目录:

bash
cd
ls -ld -- "$chapter1_lab_root"
rm -r -- "$chapter1_lab_root"

错误本身不会自动教人;记录命令、阶段、第一条诊断和产物位置,再复验一次,才会把问题缩小。

→ 下一步:变量与类型

Built with VitePress | Software Systems Atlas