跳到内容

第 1 卷 / 第 2 章 / 第 4 课

2.4 实验:变量、类型与转换 ​

前置知识:本章前三页

预计时间:40 分钟

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

完成标志:完成四项必做实验并记录预测与实际结果

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:

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);
    }
}
bash
javac -d out Profile.java
java -cp out Profile

然后分别尝试:

  • 在打印前给 age 一个新整数值;
  • 给 age 赋字符串,观察编译错误;
  • 再给 birthYear 赋值,观察 final 约束。

每次只制造一个故障,记录第一条诊断后恢复源码。再把 age 的声明移进一个独立的 { ... } 块,保留块外的打印语句,观察超出作用域后的编译错误。

必做二:范围、溢出与精确检测 ​

保存为 IntegerBoundaries.java:

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");
        }
    }
}

编译并运行:

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

预期输出:

text
2147483647
-2147483648
overflow detected

在实验记录中解释:普通 + 与 Math.addExact 的失败策略有什么不同?

必做三:浮点精度与十进制规则 ​

保存为 DecimalDemo.java:

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);
    }
}

编译并运行:

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

预期输出:

text
0.30000000000000004
false
0.3

不要得出“double 不能用”的结论。写下两种适用场景:允许测量误差的数值计算,以及必须遵循精确十进制规则的业务计算。

必做四:提升、窄化与文本解析 ​

保存为 ConversionLab.java:

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);
    }
}

先预测四行输出,再运行:

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

预期输出:

text
30
-24
9007199254740992
42

这四行分别验证二元数值提升、整数窄化、拓宽后的精度损失和文本解析。

选做一:var 的边界 ​

java
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 ​

java
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。

温度转换器 ​

java
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 variablevar 是否有可推断且非 null 的初始化式

诊断原文可能随 JDK 变化,优先判断编译期还是运行期,再看第一条关键原因。

安全清理 ​

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

仅在 pwd 确认当前为本实验目录后删除本实验的 out。不要扩大路径,也不要删除其他章节产物。

验收与清理 ​

确认自己实际观察过普通溢出与 exact 方法的不同结果、byte + byte 的提升、拓宽后的精度损失,以及一个符号占用两个 char 的情况。然后清理整个实验目录:

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

下一章把值组合成计算和判断。

→ 下一步:表达式与操作符

Built with VitePress | Software Systems Atlas