🍅 作者简介:哪吒,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: 只有必要参数
- 构造函数 2: 添加年份
- 构造函数 3: 添加颜色
- 构造函数 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、缺乏不可变性支持
不可变对象是指对象一旦创建,其状态就不能再改变。不可变对象具有许多优点:
- 线程安全:不需要同步,多个线程可以安全地共享不可变对象。
- 简单性:不用担心对象状态在意外的时间点被改变。
- 更易于使用:因为对象状态不会改变,减少了逻辑上的复杂性。
如果我们希望创建不可变对象,则需要在对象创建后防止其状态改变。Java Bean模式不支持这一点,因为它依赖于setter方法来修改对象的属性。
要创建不可变对象,需要遵循以下原则:
- 去除所有setter方法:对象状态在构造时完全设置。
- 将所有字段声明为final:确保字段只能在构造函数中赋值。
- 使用构造器初始化所有属性:在对象创建时就设置所有必须的属性。
三、使用构建器模式创建
构建器模式是一种创建对象的设计模式,尤其适合用于那些具有许多可选参数的类。相比于传统的构造器和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。
下面是使用构建器模式优化后的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);
}
}
}
👉 GPT功能:
- GPT-4o知识问答:支持1000+token上下文记忆功能
- 最强代码大模型Code Copilot:代码自动补全、代码优化建议、代码重构等
- DALL-E AI绘画:AI绘画 + 剪辑 = 自媒体新时代
- 私信哪吒,直接使用GPT-4o
🏆文章收录于:100天精通Java从入门到就业
哪吒数年工作总结之结晶。
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。
华为OD机试 2023B卷题库疯狂收录中,刷题点这里
刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。