创造型设计模式

本文详细探讨了创造型设计模式,包括单例、工厂、建造和原型模式。对于单例模式,分析了饿汉式和懒汉式的优缺点,并提出了优化方案,包括静态内部类构造和使用volatile关键字。工厂模式中比较了简单工厂、工厂方法和抽象工厂的差异。建造者模式关注于简化对象构建过程,隐藏复杂性。原型模式介绍了深复制和浅复制的概念及其在对象创建中的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

目录

 

创造型设计模式总览

单例设计模式

工厂设计模式

建造设计模式

原型设计模式


 

创造型设计模式总览

 

 

单例设计模式

我之前个人理解主要分为两个部分:懒汉式和饿汉式,因此从懒汉和饿汉出发进行分析

饿汉式:(线程安全)资源浪费,因为一定会维护一个创建好的对象

public class Student1 {

	// 2:成员变量初始化本身对象
	private static Student1 student = new Student1();

	// 构造私有
	private Student1() {
	}

	// 3:对外提供公共方法获取对象
	public static Student1 getSingletonInstance() {
		return student;
	}

	public void sayHello(String name) {
		System.out.println("hello," + name);
	}
}

懒汉式:(线程不安全)

public class Student2 {

	//1:构造私有
	private Student2(){}
	
	//2:定义私有静态成员变量,先不初始化
	private static Student2 student = null;
	
	//3:定义公开静态方法,获取本身对象
	public static Student2 getSingletonInstance(){
		//没有对象,再去创建
        //多线程情况下,该处容易造成线程安全问题
		if (student == null) {
			student = new Student2();
		}
		//有对象就返回已有对象
		return student;
	}
	
}

因为存在线程安全问题,首先可以想到加Synchronized,确实加入之后解决了线程安全问题,但是每次获取单例都需要进行排队,不管对象是否存在,效率极其低下

public static synchronized Student3 getSingletonInstance(){}

优化思路,只有在student对象为空的时候加锁(又产生了一个新的问题,JVM存在指令重排序说法(可能会将对象的初始化和对象引用赋值进行执行重排序)) 如果下方存在student.getName()操作,因为进行了指令重排序,对象内值还未进行初始化,则会报错

public static Student4 getSingletonInstance() {
		if (student == null) {
			// 采用这种方式,对于对象的选择会有问题
			// JVM优化机制:先分配内存空间,再初始化
			synchronized (Student4.class) {
				if (student == null) {
					student = new Student4();
					//student.setName("ss")
					//new ---- 开辟JVM中堆空间---产生堆内存地址保存到栈内存的student引用中---创建对象
				}
			}
		}
		return student;
	}

优化思路:使用静态内部类构造,因为类的构建是不会被指令重排序的

public class Student5 {

	private Student5() {}

	/*
	 * 此处使用一个内部类来维护单例 JVM在类加载的时候,是互斥的,所以可以由此保证线程安全问题
	 */
	private static class SingletonFactory {
		private static Student5 student = new Student5();
	}

	/* 获取实例 */
	public static Student5 getSingletonInstance() {
		return SingletonFactory.student;
	}

}

最优解:(面试如果能将该方法说的通透则会很加分)

前面分析过,如果在student为空的情况下加锁生成对象有可能面临指令重排序问题,因此,加入了volatile(有关volatile可百度),加入之后可以确保不会进行指令重排序+可以使得线程之间可见

public class Student6 {

    //双重检测
	private volatile static Student6 student;
	private Student6() {}
    public static Student6 getInstance(){
        if(student == null){
        // B线程检测到student不为空
            synchronized(Student6.class){
                if(student == null){
                    student = new Student6();
                    // A线程被指令重排了,刚好先赋值了;但还没执行完构造函数。
                }
            }
        }
		return student;// 后面B线程执行时将引发:对象尚未初始化错误。
    }

}

 

工厂设计模式

在此将普通工厂模式,工厂模式,抽象工厂模式一起,方便对比学习

简单工厂模式:

     工厂类是使用switch-case或者if-else进行对象匹配生成对象

     缺点:违反了设计原则-开放封闭原则,每次进行扩展都需要修改工厂方法

public static Object createObject(String name) {

   if ("cat".equals(name)) {
      return new Cat();
   } else if ("dog".equals(name)) {
      return new Dog();
   } else if ("cow".equals(name)) {
      return new Dog();
   } else {
      return null;
   }
}

 

工厂方法:只存在一个创建对象方法,不过具体实现是子类实现

// 抽象出来的动物工厂----它只负责生产一种产品
public abstract class AnimalFactory {
   // 工厂方法
   public abstract Animal createAnimal();
}

// 具体的工厂实现类
public class CatFactory extends AnimalFactory {

   @Override
   public Animal createAnimal() {
      return new Cat();
   }

 

抽象工厂模式:对比工厂模式,主要是抽象工厂方法内存在多个方法(可以类比于工厂模式只有一条流水线,抽象工厂支持多条流水线)

缺点:在抽象工厂内加入一个方法,则所有实现类都需要添加该方法的实现

public abstract class IceCreamFactory {

    /**
     * @Description: 可以生产大冰激凌,也可以生产小冰淇淋
     * @author: Caofeng
     * @mail: 867403822@qq.com
     * @date: 2020-12-15 9:05
    */
    public abstract SmallIceCream createSmallIceCream();

    public abstract BigIceCream createBigIceCream();
}

 

建造设计模式

个人理解:可以使得使用者不需要了解该对象的内部实现细节,不需要特定参数的构造方法(特别是参数内的null问题)

需要什么参数就添加什么参数即可

public class StudentBuilder {

   // 需要构建的对象
   private Student student = new Student();

   public StudentBuilder id(int id) {
      student.setId(id);
      return this;
   }

   public StudentBuilder name(String name) {
      student.setName(name);
      return this;
   }

   public StudentBuilder age(int age) {
      student.setAge(age);
      return this;
   }

   public StudentBuilder father(String fatherName) {
      Father father = new Father();
      father.setName(fatherName);
      student.setFather(father);
      return this;
   }

   // 构建对象
   public Student build() {
      return student;
   }
}

 

原型设计模式

内部主要是存在深复制和浅复制

深复制是根据二进制创建一个新对象

浅复制如果是引用率类型,则会创建新对象,但是内存指向地址还是和之前对象一致

使用原型模式的优点:1.基于二进制的拷贝生成对象,速度快   2.逃避构造函数约束,直接在内存中进行,不会经过构造函数(也是原型模式的缺点)


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * 原型模式 理解克隆的作用:在原对象的基础上,完全复制一个新的对象(属性都是新的)
 * 浅复制:新对象的简单类型和String类型可以复制为新的,但是引用对象还是喝原对象的一样。 深复制:完全复制一个新的对象(属性都是新的)
 * 
 * @author think
 *
 */
public class Prototype implements Cloneable, Serializable {

   private static final long serialVersionUID = 1L;

   // 简单类型或者String类型
   private String name;

   // 引用类型
   private SerializableObject object;

   @Override
   protected Object clone() throws CloneNotSupportedException {
      return super.clone();
   }
   
   /* 浅复制 */
   public Object shallowClone() throws CloneNotSupportedException {
      // super.clone()其实就是调用了Object对象的clone方法
      // Object对象的clone方法是调用了native方法去在JVM中实现对象复制。
      Prototype proto = (Prototype) super.clone();
      return proto;
   }

   /*
    * 深复制
    * 
    * 要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
    */
   public Object deepClone() throws IOException, ClassNotFoundException {

      /* 将对象序列化到二进制流 */
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(this);

      /* 从二进制流中读出产生的新对象 */
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
      return ois.readObject();
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public SerializableObject getObject() {
      return object;
   }

   public void setObject(SerializableObject object) {
      this.object = object;
   }
   
   
}

//定义一个类,为了演示深浅复制
class SerializableObject implements Serializable {
   private static final long serialVersionUID = 1L;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值