一、介绍
原型模式(Prototype Pattern)是一种对象创建型模式,它是使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
它的工作原理很简单:将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程。
1、 意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
2、何时使用
(1)当一个系统应该独立于它的产品创建,构成和表示时。
(2)当要实例化的类是在运行时刻指定时,例如,通过动态加载。
(3)为了避免创建一个与产品类层次平行的工厂类层次时。
(4)当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
3、如何解决
利用已有的一个原型对象,快速地生成和原型对象一样的实例。
4、实现
(1)实现克隆操作,在 JAVA 继承 Cloneable,重写 clone
(2)原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。
二、原型模式包含3个角色
1、Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类也可以是接口,甚至还可以是具体实现类。
2、ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
3、Client(客户类):在客户类中,让一个原型对象克隆自身从而创建一个新的对象,只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。
三、浅克隆和深克隆
1、浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
2、深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
四、 Java语言中的clone()方法和Cloneable接口
1、在Java语言中,所有的Java类均继承自java.lang.Object类,Object类提供了一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供clone()方法来实现对象的浅克隆。
2、需要注意的是能够实现克隆的Java类都必须实现一个标识接口Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将会抛出一个CloneNotSupportedException异常。如下代码所示:
public class ConcretePrototype implements Cloneable {
public Prototype clone() {
Object object = null;
try {
object = super.clone(); // 浅克隆
} catch (CloneNotSupportedException exception) {
System.err.println("Not support Cloneable");
}
return (Prototype)object;
}
}
3、为了获取对象的一个克隆,可以直接利用Object类的clone()方法,其具体步骤如下:
(1)在派生类中覆盖基类的clone()方法,并声明为public。
(2)在派生类的clone()方法中调用super.clone()。
(3)派生类需实现Cloneable接口。
此时,Object类相当于抽象原型类,所有实现了Cloneable接口的类相当于具体原型类。
五、方法覆盖
1、方法覆盖(Override)是指子类重新定义父类中已有的方法,使得该方法在子类中具有不同的实现。方法覆盖是实现多态的一种方式,它允许我们在不改变原有代码的情况下,通过重写父类方法来实现子类特有的行为。在Java中,方法覆盖需要满足两个条件:方法名、参数列表和返回值类型必须与父类中被覆盖的方法相同,访问权限不能低于父类中被覆盖的方法。
2、方法覆盖的实现原理是通过动态绑定来实现的。在程序运行时,虚拟机会根据对象的实际类型来确定调用哪个方法。如果子类中定义了与父类中同名、同参数列表、同返回值类型的方法,那么在调用该方法时,虚拟机会优先调用子类中的方法,而不是父类中的方法。
3、方法覆盖的使用可以遵循开闭原则,即对扩展开放,对修改关闭。通过方法覆盖,我们可以在不修改父类代码的情况下,扩展父类的功能,实现子类特有的行为。
4、方法覆盖也称为重写,重写即子类重新定义了父类的方法。
5、重写:
(1)重写的方法必须与原方法有相同的方法名、参数列表和返回值类型(Java SE5之后返回值类型可以是 其类型的子类型)
(2)被重写的方法不能是final类型,因为final类型无法重写
(3)被重写的方法不能是private,因为private无法继承,而继承是重写的前提
(4)被重写的方法不能为static, 如果父类中的方法为静态,而子类的方法不是静态的,但是两个方法除了这一点其他都满足重写条件,那么会发生编译错误,反之亦然。如果子类和父类中的方法都是静态的,并且满足重写条件,但仍然不会发生重写,因为静态方法是在编译时把静态方法和类的引用进行匹配。
(5)重写的方法的访问权限不能小于原方法
(6)重写抛出的异常的范围不能大于原方法
(7)重写是在运行是发生的,JVM会在代码运行时作出决定。