Java中,既然有构造器了,为什么还要有生成器?

在这里插入图片描述

🍅 作者简介:哪吒,CSDN2021博客之星亚军🏆、新星计划导师✌、博客专家💪

🍅 哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

🍅 技术交流:定期更新Java硬核干货,不定期送书活动

🍅 关注公众号【哪吒编程】,回复 1024 ,获取《10万字208道Java经典面试题总结(附答案)》2024修订版pdf,背题更方便,一文在手,面试我有

一、重叠构造器

上一篇提到《为什么阿里开发手册推荐用静态工厂方法代替构造器?》,静态工厂和构造器有一个共同的缺点,那就是当参数非常多时,写起来非常的繁琐,复杂。

我们有的时候会通过重叠构造器的方式进行优化。

假设我们要创建一个名为 Car 的类,该类具有很多参数。

通过重叠构造器模式,我们可以创建多个构造函数,每个构造函数只添加一个新的参数。这样,用户可以选择最适合他们需求的构造函数。

public class Car {
    private String make;//汽车品牌
    private String model;//汽车型号
    private int year;//制造年份
    private String color;//颜色
    private double engineSize;//发动机大小
    private boolean isElectric;//是否是电动车

    // 构造函数 1: 只有必要参数
    public Car(String make, String model) {
        this(make, model, 0); // 默认年份为0,表示未指定
    }

    // 构造函数 2: 添加年份
    public Car(String make, String model, int year) {
        this(make, model, year, "Unknown"); // 默认颜色为 "Unknown"
    }

    // 构造函数 3: 添加颜色
    public Car(String make, String model, int year, String color) {
        this(make, model, year, color, 0.0); // 默认发动机大小为0.0
    }

    // 构造函数 4: 添加发动机大小
    public Car(String make, String model, int year, String color, double engineSize) {
        this(make, model, year, color, engineSize, false); // 默认不是电动车
    }
}
  1. 构造函数 1: 只有必要参数
  2. 构造函数 2: 添加年份
  3. 构造函数 3: 添加颜色
  4. 构造函数 4: 添加发动机大小

最后一个构造器包含所有的可选参数。

重叠构造器模式可以实现功能需求,但是当参数的数量非常多时,代码写起来就会非常繁琐,要记住参数的顺序和含义,阅读代码的人就更难理解它的含义了。一长串类型相同的参数可能会导致一些不可预知的错误。

二、Java Bean Set赋值

其实,这种情况是不适合通过构造器实例化对象的,我们还可以通过Java Bean Set赋值的方式实例化对象。

public static void main(String[] args) {
    Car car = new Car();
    car.setMake("大众");
    car.setColor("白色");
    car.setElectric(false);
    car.setEngineSize(2.0);
    car.setModel("探岳");
}

这种代码没有重叠构造器模式的那些缺点,创建实例也很简单,虽然有些冗长,但生成的代码不难阅读,目前,大部分CRUD程序员都是这么写的。

但是,Java Bean Set赋值的方式存在几个问题。

1、可变性问题

当使用setter方法来设置对象属性时,实际的对象创建和属性设置是分成多个步骤执行的。每一步中,状态都可能是不完整的。

在多线程环境下尤其需要注意,因为一个线程可能正在设置对象属性,而另一个线程可能会同时读取该对象的属性。这种情况下,读取线程可能会获得不完整或错误的属性数据,导致数据竞争和逻辑错误。

Car car = new Car();
car.setMake("大众");
// 假设这里调用一个依赖于完整对象的方法
processCar(car); // 如果processCar依赖于car的完整状态,则可能出错
car.setColor("白色");
car.setElectric(false);
car.setEngineSize(2.0);
car.setModel("探岳");

2、缺乏强制性

没有办法强制要求调用者设置所有必要的属性。这可能导致对象处于不完整或不一致的状态,因为某些关键属性可能未被设置。

3、代码不够简洁

需要多次调用setter方法,使得代码冗长且不够简洁,尤其是在需要设置许多属性时。

4、缺乏不可变性支持

不可变对象是指对象一旦创建,其状态就不能再改变。不可变对象具有许多优点:

  1. 线程安全:不需要同步,多个线程可以安全地共享不可变对象。
  2. 简单性:不用担心对象状态在意外的时间点被改变。
  3. 更易于使用:因为对象状态不会改变,减少了逻辑上的复杂性。

如果我们希望创建不可变对象,则需要在对象创建后防止其状态改变。Java Bean模式不支持这一点,因为它依赖于setter方法来修改对象的属性。

要创建不可变对象,需要遵循以下原则:

  1. 去除所有setter方法:对象状态在构造时完全设置。
  2. 将所有字段声明为final:确保字段只能在构造函数中赋值。
  3. 使用构造器初始化所有属性:在对象创建时就设置所有必须的属性。

三、使用构建器模式创建

构建器模式是一种创建对象的设计模式,尤其适合用于那些具有许多可选参数的类。相比于传统的构造器和Java Bean的setter方法,构建器模式提供了一种更灵活、可读性更强的方式来创建对象。

使用构建器模式创建有哪些优点?

1、链式调用

构建器模式允许链式调用,代码更加简洁和易读。用户可以直观地看到每个参数的设置。

Car car = new Car.Builder("大众", "探岳")
        .year(2024)
        .color("白色")
        .engineSize(2.0)
        .isElectric(false)
        .build();

2、避免构造函数重载的复杂性

当类具有许多可选参数时,使用传统构造器需要大量的重载来覆盖所有可能的参数组合,这使得代码复杂且难以维护。

// 使用构造器重载时的复杂性
Car car1 = new Car("大众", "探岳");
Car car2 = new Car("大众", "探岳", 2024);
Car car3 = new Car("大众", "探岳", 2024, "白色");
// 随着参数增加,构造器重载变得不切实际

3、参数的可控性

构建器模式明确地区分必选参数和可选参数,构建器的构造函数可以只包含必需参数,而可选参数通过链式方法进行设置。

// 在构建器模式中,make和model是必选参数
Car.Builder builder = new Car.Builder("大众", "探岳");

4、默认值设置

可以在构建器中为可选参数提供合理的默认值,这样用户在不需要特定设置时可以省略这些参数。

5、对象的不可变性

通过构建器模式,可以创建不可变对象,确保对象在创建后不能再被修改。这提高了对象的安全性和可靠性,特别是在多线程环境中。

public final class Car {
    private final String make;
    private final String model;
    // 所有字段都是final且没有setter方法
}

6、易于扩展

如果需要为类添加新的可选参数,只需在构建器中添加新的方法,而不需要修改现有的构造器或类结构。

public Builder sunroof(boolean hasSunroof) {
    this.hasSunroof = hasSunroof;
    return this;
}

7、防止对象处于不完整状态

通过build()方法统一完成对象创建,避免对象在创建过程中处于不完整或不一致的状态,防止在设置所有参数之前使用对象。

通过使用构造函数或构建器模式,可以创建不可变对象,确保对象状态在创建后不被改变,从而提高线程安全性和简化对象的使用逻辑。这种设计模式特别适用于需要不可变性的场景,能够有效地规避Java Bean模式中的缺点。

四、构建器模式代码实例

我们通过ChatGPT对代码进行一个优化。

上传Car.class文件,并问下ChatGPT:使用构建器模式创建,优化代码Car。

问下ChatGPT:使用构建器模式创建,优化代码Car

下面是使用构建器模式优化后的Car类:

package com.guor.bean;

public final class Car {
    private final String make;
    private final String model;
    private final int year;
    private final String color;
    private final double engineSize;
    private final boolean isElectric;

    private Car(Builder builder) {
        this.make = builder.make;
        this.model = builder.model;
        this.year = builder.year;
        this.color = builder.color;
        this.engineSize = builder.engineSize;
        this.isElectric = builder.isElectric;
    }

    public String getMake() {
        return make;
    }

    public String getModel() {
        return model;
    }

    public int getYear() {
        return year;
    }

    public String getColor() {
        return color;
    }

    public double getEngineSize() {
        return engineSize;
    }

    public boolean isElectric() {
        return isElectric;
    }

    @Override
    public String toString() {
        return "Car{" +
                "make='" + make + '\'' +
                ", model='" + model + '\'' +
                ", year=" + year +
                ", color='" + color + '\'' +
                ", engineSize=" + engineSize +
                ", isElectric=" + isElectric +
                '}';
    }

    public static class Builder {
        // 必选参数
        private final String make;
        private final String model;

        // 可选参数 - 初始化为默认值
        private int year = 2020;
        private String color = "Unknown";
        private double engineSize = 1.5;
        private boolean isElectric = false;

        public Builder(String make, String model) {
            this.make = make;
            this.model = model;
        }

        public Builder year(int year) {
            this.year = year;
            return this;
        }

        public Builder color(String color) {
            this.color = color;
            return this;
        }

        public Builder engineSize(double engineSize) {
            this.engineSize = engineSize;
            return this;
        }

        public Builder isElectric(boolean isElectric) {
            this.isElectric = isElectric;
            return this;
        }

        public Car build() {
            return new Car(this);
        }
    }
}

问下ChatGPT:使用构建器模式创建,优化代码Car

👉 GPT功能:

  1. GPT-4o知识问答:支持1000+token上下文记忆功能
  2. 最强代码大模型Code Copilot:代码自动补全、代码优化建议、代码重构等
  3. DALL-E AI绘画:AI绘画 + 剪辑 = 自媒体新时代
  4. 私信哪吒,直接使用GPT-4o

在这里插入图片描述


🏆文章收录于:100天精通Java从入门到就业

哪吒数年工作总结之结晶。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

华为OD机试 2023B卷题库疯狂收录中,刷题点这里

刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。

点击下方名片,回复1024,获取《10万字208道Java经典面试题总结(2024修订版).pdf 》

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值