第23章

Java构造函数

掌握构造函数的使用和重载,深入理解对象初始化过程

学习目标

构造函数基础概念

构造函数是Java中用于初始化对象的特殊方法。当创建类的实例时,构造函数会被自动调用,用于设置对象的初始状态。构造函数具有与类名相同的名称,且没有返回类型。

默认构造函数

语法示例:
public class Student {
    private String name;
    
    // 默认构造函数
    public Student() {
        this.name = "Unknown";
    }
}
  • 无参数的构造函数
  • 提供对象的默认初始化
  • 如果没有定义构造函数,Java会自动提供
  • 通常用于设置默认值

参数化构造函数

语法示例:
public class Student {
    private String name;
    private int age;
    
    // 参数化构造函数
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 接受参数进行初始化
  • 允许创建时指定对象状态
  • 提供更灵活的对象创建方式
  • 可以进行参数验证

构造函数重载

语法示例:
public class Student {
    private String name;
    private int age;
    
    public Student() {
        this("Unknown", 0);
    }
    
    public Student(String name) {
        this(name, 0);
    }
    
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 同一个类中定义多个构造函数
  • 参数列表必须不同
  • 提供多种对象创建方式
  • 使用this()调用其他构造函数

构造函数链式调用

构造函数链式调用是指在一个构造函数中调用同一个类的另一个构造函数。这种技术可以避免代码重复,提高代码的可维护性。

this()调用规则

  • 位置要求:this()调用必须是构造函数的第一条语句
  • 参数匹配:根据参数列表匹配对应的构造函数
  • 避免循环:不能形成构造函数之间的循环调用
  • 代码复用:将通用的初始化逻辑集中在一个构造函数中
构造函数链式调用示例:
public class Rectangle {
    private double width;
    private double height;
    private String color;
    
    // 最完整的构造函数
    public Rectangle(double width, double height, String color) {
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("Width and height must be positive");
        }
        this.width = width;
        this.height = height;
        this.color = color != null ? color : "white";
    }
    
    // 调用完整构造函数,使用默认颜色
    public Rectangle(double width, double height) {
        this(width, height, "white");
    }
    
    // 创建正方形,调用两参数构造函数
    public Rectangle(double side) {
        this(side, side);
    }
    
    // 默认构造函数,创建单位正方形
    public Rectangle() {
        this(1.0);
    }
}

构造函数参数验证

在构造函数中进行参数验证是确保对象状态有效性的重要手段。通过验证输入参数,可以防止创建无效的对象实例。

参数验证的重要性

  • 防止创建无效状态的对象
  • 提供清晰的错误信息
  • 遵循"快速失败"原则
  • 提高代码的健壮性
参数验证示例:
public class BankAccount {
    private String accountNumber;
    private String ownerName;
    private double balance;
    
    public BankAccount(String accountNumber, String ownerName, double initialBalance) {
        // 验证账号
        if (accountNumber == null || accountNumber.trim().isEmpty()) {
            throw new IllegalArgumentException("Account number cannot be null or empty");
        }
        if (!accountNumber.matches("\\d{10}")) {
            throw new IllegalArgumentException("Account number must be 10 digits");
        }
        
        // 验证姓名
        if (ownerName == null || ownerName.trim().isEmpty()) {
            throw new IllegalArgumentException("Owner name cannot be null or empty");
        }
        if (ownerName.length() > 50) {
            throw new IllegalArgumentException("Owner name cannot exceed 50 characters");
        }
        
        // 验证余额
        if (initialBalance < 0) {
            throw new IllegalArgumentException("Initial balance cannot be negative");
        }
        
        // 设置属性
        this.accountNumber = accountNumber;
        this.ownerName = ownerName.trim();
        this.balance = initialBalance;
    }
}

完整代码示例

以下是一个综合运用构造函数各种特性的完整示例,展示了构造函数的定义、重载、链式调用和参数验证。

Person类完整示例:
public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private String email;
    
    // 完整参数构造函数
    public Person(String firstName, String lastName, int age, String email) {
        validateName(firstName, "First name");
        validateName(lastName, "Last name");
        validateAge(age);
        validateEmail(email);
        
        this.firstName = firstName.trim();
        this.lastName = lastName.trim();
        this.age = age;
        this.email = email.toLowerCase().trim();
    }
    
    // 三参数构造函数(无邮箱)
    public Person(String firstName, String lastName, int age) {
        this(firstName, lastName, age, null);
    }
    
    // 两参数构造函数(默认年龄)
    public Person(String firstName, String lastName) {
        this(firstName, lastName, 0, null);
    }
    
    // 私有验证方法
    private void validateName(String name, String fieldName) {
        if (name == null || name.trim().isEmpty()) {
            throw new IllegalArgumentException(fieldName + " cannot be null or empty");
        }
        if (name.length() > 30) {
            throw new IllegalArgumentException(fieldName + " cannot exceed 30 characters");
        }
    }
    
    private void validateAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Age must be between 0 and 150");
        }
    }
    
    private void validateEmail(String email) {
        if (email != null && !email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
            throw new IllegalArgumentException("Invalid email format");
        }
    }
    
    // Getter方法
    public String getFullName() {
        return firstName + " " + lastName;
    }
    
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }
    public String getEmail() { return email; }
    
    @Override
    public String toString() {
        return String.format("Person{name='%s', age=%d, email='%s'}", 
                           getFullName(), age, email != null ? email : "N/A");
    }
}
💻 查看完整代码 - 在线IDE体验

构造函数最佳实践

构造函数设计原则

好的实践

// 参数验证
public Student(String name, int age) {
    if (name == null || name.trim().isEmpty()) {
        throw new IllegalArgumentException("Name cannot be empty");
    }
    if (age < 0 || age > 120) {
        throw new IllegalArgumentException("Invalid age");
    }
    this.name = name.trim();
    this.age = age;
}

// 使用构造函数链式调用
public Student(String name) {
    this(name, 18); // 默认年龄
}

// 防御性复制
public Student(List courses) {
    this.courses = new ArrayList<>(courses);
}
  • 进行参数验证
  • 使用构造函数链式调用
  • 防御性复制可变对象
  • 提供清晰的错误信息
  • 保持构造函数简洁

避免的做法

// 不进行参数验证
public Student(String name, int age) {
    this.name = name; // 可能为null
    this.age = age;   // 可能为负数
}

// 在构造函数中调用可重写方法
public Student(String name) {
    this.name = name;
    initialize(); // 危险!
}

// 构造函数过于复杂
public Student(String name, int age) {
    // 大量复杂逻辑...
    // 数据库操作...
    // 网络请求...
}
  • 不验证参数有效性
  • 在构造函数中调用可重写方法
  • 构造函数逻辑过于复杂
  • 直接赋值可变对象引用
  • 忽略异常处理

构造函数设计模式

Builder模式与构造函数

当构造函数参数过多时,可以考虑使用Builder模式来替代多个重载的构造函数,提供更清晰和灵活的对象创建方式。

Builder模式示例:
public class Computer {
    private String cpu;
    private String memory;
    private String storage;
    private String graphics;
    
    // 私有构造函数
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.storage = builder.storage;
        this.graphics = builder.graphics;
    }
    
    public static class Builder {
        private String cpu;
        private String memory;
        private String storage;
        private String graphics;
        
        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }
        
        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }
        
        public Builder storage(String storage) {
            this.storage = storage;
            return this;
        }
        
        public Builder graphics(String graphics) {
            this.graphics = graphics;
            return this;
        }
        
        public Computer build() {
            return new Computer(this);
        }
    }
}

// 使用示例
Computer computer = new Computer.Builder()
    .cpu("Intel i7")
    .memory("16GB")
    .storage("512GB SSD")
    .graphics("RTX 3080")
    .build();

本章小结