跳到内容

第 1 卷 / 第 3 章 / 第 4 课

3.4 实验:表达式与操作符 ​

前置知识:本章前三页

预计时间:40 分钟

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

完成标志:先预测再验证四组边界,并记录失败原因

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

必做一:除法和余数 ​

保存为 DivisionLab.java:

java
public class DivisionLab {
    public static void main(String[] args) {
        System.out.println(10 / 3);
        System.out.println(-10 / 3);
        System.out.println(10 / 3.0);
        System.out.println(-7 % 3);
        System.out.println(Math.floorMod(-7, 3));
    }
}
bash
javac -d out DivisionLab.java
java -cp out DivisionLab

预期输出:

text
3
-3
3.3333333333333335
-1
2

用一句话分别写出 / 和 % 对负数的规则。

必做二:赋值、提升和复合赋值 ​

保存为 AssignmentLab.java:

java
public class AssignmentLab {
    public static void main(String[] args) {
        int left;
        int right;
        left = right = 10;

        byte level = 127;
        level += 1;

        System.out.println(left + "," + right);
        System.out.println(level);
    }
}

编译并运行:

bash
javac -d out AssignmentLab.java
java -cp out AssignmentLab

预期输出:

text
10,10
-128

把 level += 1 改成 level = level + 1,确认编译失败。不要只写“语法不同”,说明右侧表达式类型与隐式窄化的差异。

必做三:短路与非短路 ​

保存为 ShortCircuitLab.java:

java
public class ShortCircuitLab {
    static boolean mark(String label, boolean value) {
        System.out.println(label);
        return value;
    }

    public static void main(String[] args) {
        boolean shortAnd = mark("A", false) && mark("B", true);
        boolean fullAnd = mark("C", false) & mark("D", true);
        boolean shortOr = mark("E", true) || mark("F", false);

        System.out.println(shortAnd);
        System.out.println(fullAnd);
        System.out.println(shortOr);
    }
}
bash
javac -d out ShortCircuitLab.java
java -cp out ShortCircuitLab

输出标签应为 A、C、D、E;B 与 F 不出现。三个结果依次为 false、false、true。

必做四:字符串身份与内容 ​

java
import java.util.Objects;

public class EqualityLab {
    public static void main(String[] args) {
        String left = new String("apple");
        String right = new String("apple");
        String missing = null;

        System.out.println(left == right);
        System.out.println(left.equals(right));
        System.out.println(Objects.equals(missing, null));
    }
}
bash
javac -d out EqualityLab.java
java -cp out EqualityLab

预期为 false、true、true。在记录中标明三次比较分别检查身份、内容和 null 安全内容契约。

选做一:前置、后置与拆分副作用 ​

java
int count = 5;
int before = count++;
int after = ++count;
System.out.println(count + "," + before + "," + after);

先预测,再验证输出 7,5,7。随后阅读但不要模仿:

java
int hardToRead = count++ + ++count;

把它拆成命名中间变量,并写出每一步的 count。

选做二:浮点比较契约 ​

为以下三组值选择容差并说明依据:

text
0.1 + 0.2 与 0.3
1_000_000.0 与 1_000_000.0001
1e-12 与 2e-12

同一个固定绝对阈值未必适合三个尺度。若没有领域误差要求,就没有足够信息宣布“相等”。

选做三:最大值 ​

java
int first = 15;
int second = 8;
int third = 22;

int maxOfFirstTwo = first >= second ? first : second;
int max = maxOfFirstTwo >= third ? maxOfFirstTwo : third;
System.out.println(max);

结果应为 22。这里使用两个命名步骤,避免嵌套三元让分支关系变模糊。实际工程也可使用 Math.max。

故障实验 ​

把三组代码分别放进 main,每次只打开一组:

java
// int divisionByZero = 1 / 0;

// byte left = 10;
// byte right = 20;
// byte invalid = left + right;

// boolean wrong = 1;

第一组可以编译;程序执行到整数除零表达式时抛出 ArithmeticException。后两组由编译器拒绝:两个 byte 变量相加后结果为 int,而 Java 也不允许用整数代替 boolean。

这里不能把第二组简写成 byte invalid = (byte) 10 + (byte) 20;。那一行的右侧是值为 30 的编译期常量表达式,落在 byte 范围内,因此赋值转换允许它编译。分别记录运行时异常和两条编译诊断,不要把失败阶段混在一起。

安全清理 ​

bash
pwd
test ! -e out || rm -r -- out

确认位于本实验目录,只删除本实验的 out。

验收与清理 ​

确认自己实际观察过正负整数除法、复合赋值窄化、短路与非短路,以及字符串身份与内容比较。保存需要的实验记录后清理:

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

下一章让布尔结果真正控制路径。

→ 下一步:条件与循环

Built with VitePress | Software Systems Atlas