3.4 实验:表达式与操作符
前置知识:本章前三页
预计时间:40 分钟
练习环境:JDK 17+ 与 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:
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));
}
}javac -d out DivisionLab.java
java -cp out DivisionLab预期输出:
3
-3
3.3333333333333335
-1
2用一句话分别写出 / 和 % 对负数的规则。
必做二:赋值、提升和复合赋值
保存为 AssignmentLab.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);
}
}编译并运行:
javac -d out AssignmentLab.java
java -cp out AssignmentLab预期输出:
10,10
-128把 level += 1 改成 level = level + 1,确认编译失败。不要只写“语法不同”,说明右侧表达式类型与隐式窄化的差异。
必做三:短路与非短路
保存为 ShortCircuitLab.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);
}
}javac -d out ShortCircuitLab.java
java -cp out ShortCircuitLab输出标签应为 A、C、D、E;B 与 F 不出现。三个结果依次为 false、false、true。
必做四:字符串身份与内容
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));
}
}javac -d out EqualityLab.java
java -cp out EqualityLab预期为 false、true、true。在记录中标明三次比较分别检查身份、内容和 null 安全内容契约。
选做一:前置、后置与拆分副作用
int count = 5;
int before = count++;
int after = ++count;
System.out.println(count + "," + before + "," + after);先预测,再验证输出 7,5,7。随后阅读但不要模仿:
int hardToRead = count++ + ++count;把它拆成命名中间变量,并写出每一步的 count。
选做二:浮点比较契约
为以下三组值选择容差并说明依据:
0.1 + 0.2 与 0.3
1_000_000.0 与 1_000_000.0001
1e-12 与 2e-12同一个固定绝对阈值未必适合三个尺度。若没有领域误差要求,就没有足够信息宣布“相等”。
选做三:最大值
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,每次只打开一组:
// 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 范围内,因此赋值转换允许它编译。分别记录运行时异常和两条编译诊断,不要把失败阶段混在一起。
安全清理
pwd
test ! -e out || rm -r -- out确认位于本实验目录,只删除本实验的 out。
验收与清理
确认自己实际观察过正负整数除法、复合赋值窄化、短路与非短路,以及字符串身份与内容比较。保存需要的实验记录后清理:
cd
ls -ld -- "$chapter3_lab_root"
rm -r -- "$chapter3_lab_root"下一章让布尔结果真正控制路径。