建造者模式
在建造者模式中,通常会定义以下接口/抽象类:
抽象建造者接口(Builder):定义了构建产品各个部分的抽象方法,通常包括创建产品和设置产品各个部分的方法。具体建造者类将实现这个接口来构建产品的各个部分。
产品接口(Product):定义了产品的抽象接口,通常包括产品的属性和行为。具体的产品类将实现这个接口来表示实际构建完成的产品。
在建造者模式中,通常涉及以下几种具体类:
具体建造者(Concrete Builder):实现了抽象建造者接口,负责实际构建产品的各个部分。具体建造者类通常包括一个产品实例以及实现构建产品各个部分的方法。
指导者(Director):负责指导构建过程,即按照一定的顺序调用具体建造者的方法来构建产品。
客户端不需要直接与具体建造者类交互,而是通过指导者类根据需要自定义构建过程。这样客户端只需与指导者类进行交互,而不必了解具体的构建细节。
指导者类通常包含一个构建方法,该方法接受一个具体建造者对象作为参数,然后按照一定的顺序调用具体建造者的方法来构建产品。指导者类根据具体的构建需求和流程,来决定调用具体建造者的方法的顺序和方式。(通常并不直接创建产品。)
建造者模式中的指导者类并不构建一个具体的产品对象,而是协调建造者来构建复杂对象。
产品实现类(Concrete Product):具体产品类,实现了产品接口,表示最终构建完成的产品。产品实现类通常由具体建造者来创建并返回给客户端
在建造者模式中,产品的组装通常由客户端代码负责。:客户端通过创建指导者(Director)对象,并将具体建造者(Concrete Builder)对象传递给指导者,来控制产品的构建过程。然后,指导者类按照一定的顺序调用具体建造者的方法来构建产品的各个部分。最后,客户端通过调用具体建造者的方法获取构建完成的产品对象。
原型模式
原型模式属于创建型设计模式。通过复制现有的实例来创建新的实例
抽象原型类(Prototype):定义了一个抽象的克隆方法。
具体原型类(ConcretePrototyoe):实现抽象原型类(接口)定义的克隆方法,提供一个具体的克隆方法来复制自己。
客户端(Client):使用原型类的对象来实现具体的操作,即通过复制原型对象来创建新的对象。
浅拷贝
浅拷贝(shallow copy)和赋值(assignment)创建对象副本的方式有着重要的区别:
对象复制的方式:
浅拷贝:浅拷贝创建一个新对象,但是对于该对象内部的元素,仅仅是复制了引用而不是复制元素本身。换句话说,浅拷贝创建了一个新的容器对象,但是该容器内的元素与原始对象中的元素指向相同的内存地址。此外的内容则是独立的副本
赋值:赋值操作仅仅是将一个对象的引用(内存地址)赋给另一个变量,两者指向同一块内存区域。因此,修改其中一个变量的内容会影响到另一个变量。
影响范围:
浅拷贝:对于可变对象(如列表、字典等),浅拷贝会创建一个新对象,但是其内部的可变元素(如列表中的元素)仍然会被共享。这意味着对于这些可变元素的修改会影响到原始对象和副本对象。
赋值:赋值操作会使两个变量指向同一块内存区域,因此对任何一个变量进行修改都会影响到另一个变量。
适用场景:
浅拷贝:适用于需要复制对象但又不希望复制其内部元素的场景,或者在需要修改副本对象而不影响原始对象的情况下使用。
赋值:适用于简单的对象赋值操作,或者在需要两个变量共享相同对象的情况下使用。
浅拷贝是使用默认的clone()方法来实现
在Java中,原型模式的核心是通过实现 Cloneable 接口来实现对象的克隆。这个接口没有定义任何方法,但是它表明了该对象是可复制的
sheep = (Sheep) super.clone();
new:每次使用 "new" 关键字创建对象时,都会得到一个全新的对象实例,它与之前创建的对象完全独立。
clone:使用 "clone" 方法创建的对象是原始对象的副本,但是它们之间并不是完全独立的。通常情况下,副本对象和原始对象之间共享相同的内部状态,但是它们在内存中拥有不同的引用地址
new:通常用于创建全新的对象实例,无需共享状态或复制原始对象的情况。
clone:适用于需要创建对象副本的情况,但又希望保留对象内部状态的情况。例如,在需要使用原始对象的副本进行修改而不影响原始对象的情况下,可以使用 "clone" 方法。
单例模式
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象
有两种类型:
懒汉式:在真正需要使用对象时才去创建该单例类对象
饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
因为需要两次判空,且对类对象加锁,该懒汉式写法也被称为:Double Check(双重校验) + Lock(加锁)
使用volatile防止指令重排
创建一个对象,在JVM中会经过三步:
(1)为singleton分配内存空间
(2)初始化singleton对象
(3)将singleton指向分配好的内存空间
指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能
在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常
使用volatile关键字可以防止指令重排序
1.如果一个类不能通过NEW创建实例对象(从外部调用构造函数),要定义为私有类型构造函数
2.私有静态成员变量保存唯一实例
3.向外部提供一个访问接口去访问实例,公有的静态共享方法。定义为static