目录
创造型设计模式总览
单例设计模式
我之前个人理解主要分为两个部分:懒汉式和饿汉式,因此从懒汉和饿汉出发进行分析
饿汉式:(线程安全)资源浪费,因为一定会维护一个创建好的对象
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;
}