学习前提

理解Java方法、参数和字段;能阅读构造器调用。

本课完成后你能做什么

用构造器和方法维护对象的不变条件。

先理解机制

类描述状态和行为,对象是类的具体实例。每个Progress对象都有独立done计数;如果把它声明为static,多个实例就会共享同一份计数。

private阻止调用者直接设置字段。构造器验证total,complete限制上界,因而所有公开操作都维持0<=done<=total。封装的价值在于维护规则,不是机械生成所有setter。

零总数的百分比需要产品定义。本例定义为0;计算中先转换为long再乘100,避免int乘法提前溢出。整数除法向零截断,本例非负数即向下取整。

可运行示例

使用JDK 21。将代码保存为Main.java,在独立练习目录执行 javac -encoding UTF-8 Main.java,再执行 java Main。每章独立运行,不把多个public Main类放进同一文件。


public class Main {
    static class Progress {
        private final int total;
        private int done;
        Progress(int total) {
            if (total < 0) throw new IllegalArgumentException("negative total");
            this.total = total;
        }
        void complete() { if (done < total) done++; }
        int percent() { return total == 0 ? 0 : (int) ((long) done * 100 / total); }
    }
    public static void main(String[] args) {
        Progress first = new Progress(4);
        Progress second = new Progress(4);
        first.complete();
        System.out.println(first.percent() + " " + second.percent());
        Progress empty = new Progress(0);
        empty.complete();
        System.out.println(empty.percent());
    }
}

推演与验证

两个对象输出25和0,说明状态独立。总数为0时complete不增加计数,仍输出0。

常见错误与边界

该类没有线程同步,适用于本课单线程程序。final限制字段重新赋值,并不自动让任意对象深度不可变。

动手练习

对总数2连续调用complete三次,并验证负数构造失败。

参考解法与理由

第三次调用被上界条件挡住,结果100。new Progress(-1)抛出IllegalArgumentException;调用者应修正输入。

进一步阅读