创建型模式关注创建对象。
创建型模式将对象的创建和使用分离,使用者无需关注对象的创建细节。
创建型模式包括 5 种:单例模式、工厂方法模式、抽象工厂模式、原型模式、建造者模式。
单例模式
类负责创建自己的唯一对象,并提供访问唯一对象的方法。
注意两点:私有化构造方法;提供获取实例的方法。
实现方式
饿汉式
对象随着类的加载而创建,存在内存浪费现象。
// 饿汉式-静态成员变量
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 创建实例
private static final Singleton instance = new Singleton();
// 提供获取实例的方法
public static Singleton getInstance() {
return instance;
}
}
// 饿汉式-静态代码块
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 创建实例
private static Singleton instance;
// 静态代码块中进行赋值
static {
// 实例化前可进行额外操作
instance = new Singleton();
}
// 提供获取实例的方法
public static Singleton getInstance() {
return instance;
}
}
在静态代码块中实例化对象,可在实例化对象前后执行额外操作,如加载配置文件。
懒汉式
对象不随类的加载而创建,而是在第一次使用时创建。
/**
* 懒汉式-线程不安全
* 多线程环境下存在线程安全问题
*/
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 声明变量
private static Singleton instance;
// 提供获取实例的方法
public static Singleton getInstance() {
// 在使用时创建对象
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
/**
* 懒汉式-synchronized
* 通过synchronized关键字解决线程安全问题,但执行效率较低
*/
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 声明变量
private static Singleton instance;
// 提供获取实例的方法
public static synchronized Singleton getInstance() {
// 在使用时创建对象
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
/**
* 懒汉式-双重检查锁
* 第一次读不加锁,缩小锁的范围,提高执行效率,但,指令重排可能导致空指针
*/
public class Singleton {
// 私有化构造方法
private Singleton() {}
// 声明变量
private static Singleton instance;
// 提供获取实例的方法
public static Singleton getInstance() {
// 第一次判断-无锁
if (instance == null) {
synchronized (Singleton.class) {
// 第二次判断-有锁
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
/**
* 懒汉式-双重检查锁()
* 使用volatile解决指令重排导致的空指针问题
*/
public class Singleton {
// 私有化构造方法
private Singleton() { }
// 声明变量
// volatile防止指令重排导致的空指针问题
private static volatile Singleton instance;
// 提供获取实例的方法
public static Singleton getInstance() {
// 第一次判断-无锁
if (instance == null) {
synchronized (Singleton.class) {
// 第二次判断-有锁
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类方式
/**
* 懒汉式-静态内部类方式
*/
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 定义静态内部类
private static class SingletonHolder {
// 声明并初始化外部类对象
private static final Singleton INSTANCE = new Singleton();
}
// 提供获取实例的方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
JVM 在加载外部类的过程中, 不会加载静态内部类,静态内部类只有在属性/方法被调用时才会被加载,并初始化其静态属性。
枚举方式
/**
* 饿汉式-枚举方式
*/
public enum Singleton {
INSTANCE
}
枚举类型是线程安全的,并且只会装载一次。
破坏单例
除枚举方式外,其余方式实现的单例模式可通过序列化或反射破坏,即可创建多个对象。
序列化方式
将对象序列化后,每次反序列化得到的对象都是新对象。
public static void writeObject2File(Singleton obj){
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(obj);
}
public static Singleton readObjectFromFile(){
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
return (Singleton) ois.readObject();
}
public static void main(String[] args) {
Singleton o1 = Singleton.getInstance();
// 序列化对象
writeObject2File(o1);
// 反序列化对象
Singleton o2 = readObjectFromFile();
Singleton o3 = readObjectFromFile();
// o1!=o2 o1!=o3 o2!=o3
}
readResolve() 方法会在反序列化时被反射调用,如果定义 readResolve(),则将其返回值作为反序列化得到的对象,否则将创建新的对象。
public class Singleton implements Serializable {
//...
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
跟踪 readObject() 源码会发现如下代码:
// 创建新对象
obj = desc.isInstantiable() ? desc.newInstance() : null;
...
// 如果有readResolve()方法则将其返回值赋给obj
if (... && desc.hasReadResolveMethod()){
Object rep = desc.invokeReadResolve(obj);
...
if (rep != obj) {
....
handles.setObject(passHandle, obj = rep);
}
}
// obj作为反序列化得到的对象
return obj;
反射方式
通过反射创建的对象都是新对象。
public static void main(String[] args) {
// 获取字节码对象
Class<Singleton> clazz = Singleton.class;
// 获取无参构造方法
Constructor<Singleton> cons = clazz.getDeclaredConstructor();
// 取消访问检查
cons.setAccessible(true);
// 创建Singleton对象
Singleton o1 = cons.newInstance();
Singleton o2 = cons.newInstance();
// o1!=o2
}
以上代码利用反射通过构造方法创建对象,当 instance 不为 null 时,禁止通过构造方法创建对象即可防止破坏单例模式。
// 非静态内部类方式
public class Singleton {
private Singleton() {
// 在构造方法中对instance进行判断
if (instance != null) {
throw new RuntimeException();
}
}
}
// 静态内部类方式
public class Singleton implements Serializable {
// flag标识Singleton构造方法是否已经被调用
private static boolean flag = false;
// 私有化构造方法
private Singleton() {
synchronized (Singleton.class) {
// 在构造方法中对flag进行判断
if (flag) {
throw new RuntimeException();
}
flag = true;
}
}
}
源码寻迹
JDK 中的 Runtime 类使用饿汉式实现单例模式
public class Runtime {
private static final Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
}
工厂模式
工厂根据使用方的意图创建对象。
实现方式
简单工厂模式
简单工厂模式不是一种设计模式,更像是一种编程习惯,违背开闭原则。
简单工厂模式的角色
- 抽象产品:定义产品规范。
- 具体产品:实现抽象产品。
- 具体工厂:提供获取产品的方法。
新增具体产品时,需要修改具体工厂,违背开闭原则。
public class ComputerFactory {
// 可定义为非静态方法
public static Computer createComputer(String purpose) {
// 根据意图创建对象
Computer computer = null;
if ("MacBook".equals(purpose)) {
computer = new MacBook();
}
else if ("ThinkPad".equals(purpose)) {
computer = new ThinkPad();
}
return computer;
}
}
// 使用者
Computer computer = ComputerFactory.createComputer("MacBook");
工厂方法模式
工厂方法模式的角色
- 抽象工厂(Abstract Factory):定义创建产品的接口。
- 具体工厂(Concrete Factory):实现抽象工厂。
- 抽象产品(Product):定义产品规范。
- 具体产品(Concrete Product):实现抽象产品。
工厂方法模式新增具体产品时,只需新增具体工厂,无需修改原有代码,满足开闭原则。
工厂方法模式使产品类的实例化延迟到抽象工厂的子类。
// 抽象工厂
public interface ComputerFactory {
public Computer createComputer();
}
// 具体工厂:专门创建MacBook
public class MacBookComputerFactory implements ComputerFactory {
@Override
public Computer createComputer() {
return new MacBook();
}
}
// 具体工厂:专门创建ThinkPad
public class ThinkPadComputerFactory implements ComputerFactory {...}
// 使用者
MacBookComputerFactory factory = new MacBookComputerFactory();
MacBook computer = factory.createComputer();
抽象工厂模式
抽象工厂模式的角色
- 抽象工厂(Abstract Factory):定义创建产品的接口,可创建多个产品。
- 具体工厂(Concrete Factory):实现抽象工厂。
- 抽象产品(Product):定义产品规范,有多个抽象产品。
- 具体产品(Concrete Product):实现抽象产品。
同一类的产品属于同一等级,同一工厂的产品属于同一族。
举个例子,华为工厂生产华为手机和华为电脑,苹果工厂生产苹果手机和苹果电脑,华为手机和苹果手机属于统一等级,华为手机和华为电脑属于同一族。
抽象工厂模式是对工厂方法模式的拓展,其具体工厂负责生产同一族的不同等级的产品。
抽象工厂模式在增加新的等级的产品时,需要修改抽象工厂及各具体工厂,违背了开闭原则。
适用场景:分等级分族
// 抽象工厂
public interface Factory {
Computer createComputer();
Phone createPhone();
}
// 具体工厂:华为工厂
public class HuaWeiFactory implements Factory {
@Override
public Computer createComputer() {
return new MateBook();
}
@Override
public Phone createPhone() {
return new HuaWeiPhone();
}
}
// 具体工厂:苹果工厂
public class AppleFactory implements Factory {...}
// 使用者
HuaWeiFactory factory = new HuaWeiFactory();
MateBook computer = factory.createComputer();
模式拓展
通过简单工厂+配置文件的方式解除工厂对象和产品对象的耦合。
# 配置文件bean.properties
american=com.factory.MacBook
latte=com.factory.ThinkPad
public class ComputerFactory {
// 存放所有产品对象
private static Map<String,Computer> map = new HashMap();
// 加载配置文件并创建产品对象
static {
Properties p = new Properties();
InputStream is = ComputerFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
p.load(is);
// 遍历Properties集合对象
Set<Object> keys = p.keySet();
for (Object key : keys) {
// 全类名
String className = p.getProperty((String) key);
// 字节码对象
Class clazz = Class.forName(className);
// 创建产品对象
Computer obj = (Computer) clazz.newInstance();
map.put((String)key,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 提供方法获取对象
public static Computer createComputer(String name) {
// 相当于将判断操作交由map
return map.get(name);
}
}
源码寻迹
JDK 源码中的 Collection 与 Iterator 使用了工厂方法模式。
// 抽象工厂
public interface Collection<E>{
// 抽象产品
Iterator<E> iterator();
}
// 具体工厂
public class ArrayList<E> implements List<E>{
public Iterator<E> iterator() {
// 具体产品
return new Itr();
}
}
// interface List<E> extends Collection<E>
// class Itr implements Iterator<E>
原型模式
通过复制(浅拷贝/深拷贝)实例来创建和实例相同的对象。
原型模式的角色
- 抽象原型:定义 clone() 接口。
- 具体原型:实现抽象原型。
适用场景:对象创建非常复杂,待创建对象与已创建对象相似;性能和安全要求较高。
实现方式
// 抽象原型
public interface Cloneable {}
// 具体原型
public class RealizeType implements Cloneable {
@Override
public RealizeType clone() {
// 实际调用Object的clone()
return super.clone();
}
}
源码寻迹
Object 类中声明了 clone() 方法,并给出了默认实现(浅克隆)。
public class Object {
@IntrinsicCandidate
protected native Object clone();
}
建造者模式
将部件的构造(Builder)和装配(Director)分离。
建造者模式的角色
- 产品(Product):待创建的复杂对象,由多个构件组成。
- 抽象建造者(Builder):定义构造构件的接口。
- 具体建造者(Concrete Builder):实现抽象建造者。
- 指挥者(Director):装配各部件。
建造者模式可以精细控制产品创建过程,构件的构造和组装分离。
适用场景:对象复杂,构建与装配独立,构建经常变化,装配保持稳定。
实现方式
// 产品
public class Bike {
private String frame;
private String seat;
}
// 抽象建造者
public abstract class Builder {
// 声明产品
protected Bike bike = new Bike();
// 构造Frame
public abstract void buildFrame();
// 构造Seat
public abstract void buildSeat();
// 构建自行车
public abstract Bike createBike();
}
// 具体建造者
public class ABikeBuilder extends Builder {
@Override
public void buildFrame() {
bike.setFrame("A-Frame");
}
@Override
public void buildSeat() {
bike.setSeat("A-Seat");
}
@Override
public Bike createBike() {
return bike;
}
}
public class BBikeBuilder extends Builder {...}
// 指挥者
public class Director {
// 建造者
private Builder builder;
// 指挥者
public Director(Builder builder) {
this.builder = builder;
}
// 装配产品
public Bike construct() {
builder.buildFrame();
builder.buildBike();
return builder.createBike();
}
}
// 使用者
Director director = new Director(new ABikeBuilder());
Bike bike = director.construct();
注意,指挥者中装配产品的过程可能比较复杂,根据迪米特法则,装配任务不应交由使用者完成。为了简化系统结构,可将装配任务交由建造者完成,但不符合单一职责原则。
// 具体建造者
public class ABikeBuilder extends Builder {
@Override
public Bike createBike() {
this.buildFrame();
this.buildBike();
return bike;
}
}
当一个类构造器需要传入多个参数时,为提高代码可读性,可以利用建造者模式进行重构。
public class Phone {
// 多个属性
private String cpu;
private String screen;
// 私有化构造方法
private Phone(Builder builder) {
this.cpu = builder.cpu;
this.screen = builder.screen;
}
// 建造者
public static final class Builder {
// 建造者属性与对象属性相同
private String cpu;
private String screen;
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder screen(String screen) {...}
// 使用构建者创建对象
public Phone build() {
return new Phone(this);
}
}
}
Lombok 的 @Builder 注解实现上述效果。
VS 工厂模式
工厂方法模式注重对象整体的创建,建造者模式注重部件的构建以及装配。
抽象工厂模式实现对产品族中一系列产品的创建,建造者模式实现对特定产品的创建。
END
文章文档:公众号 字节幺零二四
回复关键字可获取本文文档。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。