2.4 实验:变量、类型与转换
前置知识:本章前三页
预计时间:40 分钟
练习环境:JDK 17+ 与 Bash;所有文件写入临时目录
完成标志:完成四项必做实验并记录预测与实际结果
chapter2_lab_root=$(mktemp -d)
readonly chapter2_lab_root
mkdir -p "$chapter2_lab_root/chapter02-lab/out"
cd "$chapter2_lab_root/chapter02-lab"必做一:声明、作用域与 final
保存为 Profile.java:
public class Profile {
public static void main(String[] args) {
String name = "Lin";
int age = 20;
double heightMeters = 1.72;
char initial = 'L';
boolean learningJava = true;
final int birthYear = 2006;
System.out.println(name);
System.out.println(age);
System.out.println(heightMeters);
System.out.println(initial);
System.out.println(learningJava);
System.out.println(birthYear);
}
}javac -d out Profile.java
java -cp out Profile然后分别尝试:
- 在打印前给
age一个新整数值; - 给
age赋字符串,观察编译错误; - 再给
birthYear赋值,观察final约束。
每次只制造一个故障,记录第一条诊断后恢复源码。再把 age 的声明移进一个独立的 { ... } 块,保留块外的打印语句,观察超出作用域后的编译错误。
必做二:范围、溢出与精确检测
保存为 IntegerBoundaries.java:
public class IntegerBoundaries {
public static void main(String[] args) {
int max = Integer.MAX_VALUE;
System.out.println(max);
System.out.println(max + 1);
try {
System.out.println(Math.addExact(max, 1));
} catch (ArithmeticException error) {
System.out.println("overflow detected");
}
}
}编译并运行:
javac -d out IntegerBoundaries.java
java -cp out IntegerBoundaries预期输出:
2147483647
-2147483648
overflow detected在实验记录中解释:普通 + 与 Math.addExact 的失败策略有什么不同?
必做三:浮点精度与十进制规则
保存为 DecimalDemo.java:
import java.math.BigDecimal;
public class DecimalDemo {
public static void main(String[] args) {
double binary = 0.1 + 0.2;
BigDecimal decimal = new BigDecimal("0.1")
.add(new BigDecimal("0.2"));
System.out.println(binary);
System.out.println(binary == 0.3);
System.out.println(decimal);
}
}编译并运行:
javac -d out DecimalDemo.java
java -cp out DecimalDemo预期输出:
0.30000000000000004
false
0.3不要得出“double 不能用”的结论。写下两种适用场景:允许测量误差的数值计算,以及必须遵循精确十进制规则的业务计算。
必做四:提升、窄化与文本解析
保存为 ConversionLab.java:
public class ConversionLab {
public static void main(String[] args) {
byte left = 10;
byte right = 20;
int sum = left + right;
int source = 1000;
byte narrowed = (byte) source;
long precise = 9_007_199_254_740_993L;
double widened = precise;
int parsed = Integer.parseInt("42");
System.out.println(sum);
System.out.println(narrowed);
System.out.println((long) widened);
System.out.println(parsed);
}
}先预测四行输出,再运行:
javac -d out ConversionLab.java
java -cp out ConversionLab预期输出:
30
-24
9007199254740992
42这四行分别验证二元数值提升、整数窄化、拓宽后的精度损失和文本解析。
选做一:var 的边界
public class VarDemo {
public static void main(String[] args) {
var city = "Beijing";
var count = 42;
var ratio = 0.5F;
System.out.println(city.getClass().getSimpleName());
System.out.println(count + 1);
System.out.println(ratio + 0.25F);
}
}验证后依次尝试 var missing;、var unknown = null; 和 city = 42;。三条失败的原因并不相同:缺初始化式、没有可推断类型、已推断类型不兼容。
选做二:一个符号不一定是一个 char
public class UnicodeDemo {
public static void main(String[] args) {
String symbol = "😀";
System.out.println(symbol.length());
System.out.println(symbol.codePointCount(0, symbol.length()));
System.out.printf("U+%X%n", symbol.codePointAt(0));
}
}预期:UTF-16 长度为 2,码点数量为 1,码点是 U+1F600。
温度转换器
double fahrenheit = 100.0;
double celsius = (fahrenheit - 32.0) * 5.0 / 9.0;
System.out.println(celsius);使用浮点字面量明确表达浮点运算。若需求规定显示两位小数,还需明确舍入与格式化规则;原始 double 的长小数输出不是最终用户界面规范。
排障速查
| 关键诊断 | 先检查 |
|---|---|
| variable might not have been initialized | 局部变量是否在所有读取路径之前赋值 |
| possible lossy conversion | 是否正在窄化;值域是否已校验 |
| integer number too large | 十进制字面量是否需要 L,或已超出 long |
| bad operand types | 运算符两侧类型是否支持该运算 |
| NumberFormatException | 输入文本、进制和空白处理是否符合约定 |
| cannot infer type for local variable | var 是否有可推断且非 null 的初始化式 |
诊断原文可能随 JDK 变化,优先判断编译期还是运行期,再看第一条关键原因。
安全清理
pwd
test ! -e out || rm -r -- out仅在 pwd 确认当前为本实验目录后删除本实验的 out。不要扩大路径,也不要删除其他章节产物。
验收与清理
确认自己实际观察过普通溢出与 exact 方法的不同结果、byte + byte 的提升、拓宽后的精度损失,以及一个符号占用两个 char 的情况。然后清理整个实验目录:
cd
ls -ld -- "$chapter2_lab_root"
rm -r -- "$chapter2_lab_root"下一章把值组合成计算和判断。