设计模式之原型模式

原型模式 一:原型模式概述

为什么要用原型模式:

在系统中有时候可能需要创建多个一模一样的对象,而有的对象创建过程十分复杂,或者创建对象很耗费资源亦或是创建对象十分频繁,那么这个时候就必须要解决这个问题,而原型模式则能很好的解决这个问题。

基本定义:

原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

二:原型模式原理结构图

基本角色

Prototype(抽象原型类)
是声明了克隆方法的接口,所有具体原型类的基类,既可以是接口又可以是抽象类,还可以是具体实现类。

ConcretePrototype(具体原型类)
实现抽象原型类中的克隆方法,在克隆方法中返回自己一个克隆对象。

三:深克隆与浅克隆 基本概念

浅复制(浅克隆)

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所拷贝的对象,而不复制它所引用的对象。

深复制(深克隆)

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

实现java深复制和浅复制的最关键的就是要实现Cloneable接口中的clone()方法。

如何使用clone()方法

首先我们来看一下Cloneable接口:

官方解释:

1:实现此接口则可以使用java.lang.Object 的clone()方法,否则会抛出CloneNotSupportedException 异常

2:实现此接口的类应该使用公共方法覆盖clone方法

3:此接口并不包含clone 方法,所以实现此接口并不能克隆对象,这只是一个前提,还需覆盖上面所讲的clone方法。

public interface Cloneable { }

看看Object里面的Clone()方法:

clone()方法返回的是Object类型,所以必须强制转换得到克隆后的类型

clone()方法是一个native方法,而native的效率远远高于非native方法,

可以发现clone方法被一个Protected修饰,所以可以知道必须继承Object类才能使用,而Object类是所有类的基类,也就是说所有的类都可以使用clone方法

protected native Object clone() throws CloneNotSupportedException;

小试牛刀:

public class Person { public void testClone(){ super.clone(); // 报错了 } }

事实却是clone()方法报错了,那么肯定奇怪了,既然Object是一切类的基类,并且clone的方法是Protected的,那应该是可以通过super.clone()方法去调用的,然而事实却是会抛出CloneNotSupportedException异常, 官方解释如下:

对象的类不支持Cloneable接口

覆盖方法的子类也可以抛出此异常表示无法克隆实例。

所以我们更改代码如下:

public class Person implements Cloneable{ public void testClone(){ try { super.clone(); System.out.println("克隆成功"); } catch (CloneNotSupportedException e) { System.out.println("克隆失败"); e.printStackTrace(); } } public static void main(String[] args) { Person p = new Person(); p.testClone(); } }

要注意,必须将克隆方法写在try-catch块中,因为clone方法会把异常抛出,当然程也要求我们try-catch。

java.lang.object规范中对clone方法的约定

对任何的对象x,都有x.clone() !=x 因为克隆对象与原对象不是同一个对象

对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样

如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立

对于以上三点要注意,这3项约定并没有强制执行,所以如果用户不遵循此约定,那么将会构造出不正确的克隆对象,所以根据effective java的建议:

谨慎的使用clone方法,或者尽量避免使用。

浅复制实例

对象中全部是基本类型

public class Teacher implements Cloneable{ private String name; private int age; public Teacher(String name, int age){ this.name = name; this.age = age; } // 覆盖 @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } // 客户端测试 public class test { Teacher origin = new Teacher("tony", 11); System.out.println(origin.getName()); Teacher clone = (Teacher) origin.clone(); clone.setName("clone"); System.out.println(origin.getName()); System.out.println(clone.getName()); }

结果:

tony
tony
clone

1545111039834

从运行结果和图上可以知道,克隆后的值变量会开辟新的内存地址,克隆对象修改值不会影响原来对象。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjydf.html