0Pricing
Java Academy · 课时

紧凑构造方法与验证

在紧凑构造方法中添加验证逻辑,确保数据完整性

紧凑构造方法与验证 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

紧凑构造函数

记录类中的紧凑构造函数会在组件赋值之前运行。它可以让您验证或规范化数据,而无需重复编写组件赋值代码。

标准构造函数与紧凑构造函数

标准规范构造函数会显式为组件赋值。紧凑构造函数省略参数列表和赋值语句——这些操作会在构造函数体运行后自动完成。

// Standard canonical constructor
record Range(int min, int max) {
    Range(int min, int max) {
        if (min > max) throw new IllegalArgumentException("min > max");
        this.min = min;  // explicit assignment
        this.max = max;
    }
}

// Compact constructor (same behavior, less code)
record Range2(int min, int max) {
    Range2 {  // no parameter list, no assignments
        if (min > max) throw new IllegalArgumentException("min > max");
        // components are assigned automatically after this block
    }
}

在紧凑构造函数中进行验证

紧凑构造函数是验证记录类数据的标准方式。请尽早抛出异常,以强制执行不变量。

record Email(String address) {
    Email {
        if (address == null || address.isBlank())
            throw new IllegalArgumentException("Email cannot be blank");
        if (!address.contains("@"))
            throw new IllegalArgumentException("Invalid email: " + address);
        address = address.toLowerCase().strip(); // normalize (Java 16+)
    }
}

Email e = new Email("  Alice@Example.com  ");
System.out.println(e.address()); // alice@example.com

try { new Email("not-an-email"); }
catch (IllegalArgumentException ex) { System.out.println(ex.getMessage()); }

在紧凑构造函数中进行规范化

您可以在紧凑构造函数中修改组件变量,然后再为它们赋值。这样可以在构造时规范化数据。

record PersonName(String firstName, String lastName) {
    PersonName {
        firstName = capitalize(firstName);
        lastName  = capitalize(lastName);
    }

    private static String capitalize(String s) {
        if (s == null || s.isEmpty()) return s;
        return Character.toUpperCase(s.charAt(0)) +
               s.substring(1).toLowerCase();
    }

    public String fullName() { return firstName + " " + lastName; }
}

PersonName name = new PersonName("jOHN", "DOE");
System.out.println(name.fullName()); // John Doe

防御性复制

对于数组或集合等可变组件,请在紧凑构造函数中执行防御性复制,以保持不可变性。

import java.util.*;

record Snapshot(List<String> items) {
    Snapshot {
        items = List.copyOf(items); // defensive copy — unmodifiable
    }
}

List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));
Snapshot snap = new Snapshot(mutable);
mutable.add("d"); // doesn't affect snapshot
System.out.println(snap.items()); // [a, b, c]

try {
    snap.items().add("e"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Snapshot is truly immutable!");
}

带边界的 Range 记录类

一个实用的 Range 记录类,可确保 min <= max,并提供有用的实用方法。

record Range(int min, int max) {
    Range {
        if (min > max) throw new IllegalArgumentException(
            "min (" + min + ") must be <= max (" + max + ")");
    }

    public boolean contains(int value) { return value >= min && value <= max; }
    public int size()  { return max - min; }
    public int clamp(int value) { return Math.max(min, Math.min(max, value)); }
}

Range valid = new Range(1, 10);
System.out.println(valid.contains(5));  // true
System.out.println(valid.clamp(15));    // 10
System.out.println(valid.size());       // 9

串联紧凑构造函数逻辑

对于复杂的验证逻辑,请提取辅助方法,并从紧凑构造函数中调用它们。

record CreditCard(String number, String cvv, int expiryMonth, int expiryYear) {
    CreditCard {
        validateNumber(number);
        validateCvv(cvv);
        validateExpiry(expiryMonth, expiryYear);
        number = number.replaceAll("[^0-9]", ""); // strip spaces/dashes
    }

    private static void validateNumber(String n) {
        String digits = n.replaceAll("[^0-9]", "");
        if (digits.length() < 13 || digits.length() > 19)
            throw new IllegalArgumentException("Invalid card number length");
    }
    private static void validateCvv(String cvv) {
        if (!cvv.matches("[0-9]{3,4}"))
            throw new IllegalArgumentException("Invalid CVV");
    }
    private static void validateExpiry(int m, int y) {
        if (m < 1 || m > 12) throw new IllegalArgumentException("Invalid month");
        if (y < 2024)         throw new IllegalArgumentException("Card expired");
    }
}

多种紧凑构造函数模式

紧凑构造函数中常用的验证模式。

record Percentage(double value) {
    Percentage {
        if (value < 0 || value > 100)
            throw new IllegalArgumentException(
                "Percentage must be 0-100, got: " + value);
        value = Math.round(value * 100.0) / 100.0; // round to 2 dp
    }

    public double asFraction() { return value / 100.0; }
}

Percentage tax = new Percentage(8.756);
System.out.println(tax.value());       // 8.76
System.out.println(tax.asFraction()); // 0.0876

非规范构造函数

记录类可以拥有额外的构造函数,但必须使用 this(...) 委托给规范构造函数。

record Point(double x, double y) {
    // Non-canonical constructor: origin point
    Point() { this(0.0, 0.0); }

    // Non-canonical: polar coordinates
    static Point fromPolar(double r, double theta) {
        return new Point(r * Math.cos(theta), r * Math.sin(theta));
    }

    public double distance(Point other) {
        double dx = this.x - other.x;
        double dy = this.y - other.y;
        return Math.sqrt(dx*dx + dy*dy);
    }
}

Point origin = new Point();
Point p = Point.fromPolar(5, Math.PI/4);
System.out.printf("Distance: %.2f%n", origin.distance(p)); // 5.00

不可变集合记录类

一个保存用户偏好不可变快照的记录类,并会在构造时进行验证和规范化。

import java.util.*;

record UserPreferences(String theme, Set<String> enabledFeatures, int fontSize) {
    private static final Set<String> VALID_THEMES = Set.of("light", "dark", "system");
    private static final Set<String> VALID_FEATURES = Set.of("ai", "beta", "analytics");

    UserPreferences {
        if (!VALID_THEMES.contains(theme))
            throw new IllegalArgumentException("Unknown theme: " + theme);
        if (!VALID_FEATURES.containsAll(enabledFeatures))
            throw new IllegalArgumentException("Unknown feature in: " + enabledFeatures);
        if (fontSize < 10 || fontSize > 24)
            throw new IllegalArgumentException("fontSize must be 10-24");
        enabledFeatures = Set.copyOf(enabledFeatures); // defensive copy
    }
}

紧凑构造函数的限制

在紧凑构造函数中不能执行以下操作:

  • 不能显式为组件赋值(组件会在代码块执行后自动赋值)
  • 不能调用 this() 或 super()
  • 抛出异常会阻止所有组件赋值
record Safe(int value) {
    Safe {
        // CAN: validate and modify component variables
        if (value < 0) value = 0; // normalized to 0 if negative
        // value = this.value; // NOT NEEDED — assignment happens after block
    }
}

System.out.println(new Safe(-5).value());  // 0 (normalized)
System.out.println(new Safe(10).value());  // 10

快速检查

在紧凑构造函数中对组件变量所做的修改会产生什么结果?

回顾:紧凑构造函数与验证

要点:

  • 紧凑构造函数会在组件赋值之前运行——无需显式赋值
  • 使用它们验证、规范化或防御性复制组件
  • 修改组件变量以规范化值(转为小写、去除首尾空白、复制)
  • 对于无效数据,抛出 IllegalArgumentException 以强制执行不变量
  • 对可变集合组件使用 List.copyOf / Set.copyOf
  • 非规范构造函数必须使用 this(...) 委托给规范构造函数

常见问题解答

「紧凑构造方法与验证」课时是免费的吗?

是的 — 「紧凑构造方法与验证」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「紧凑构造方法与验证」这节课中我会学到什么?

在紧凑构造方法中添加验证逻辑,确保数据完整性 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「紧凑构造方法与验证」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 认识记录类
  2. 紧凑构造方法与验证
  3. 为记录类添加自定义方法
  4. 记录类、类与 Lombok 的比较
← 返回 Java Academy