上一篇:设计模式(三)——单例模式

下一篇:设计模式(五)——建造者模式

目录

一、概要

二、代码

三、深复制和浅复制

3.1 概述

3.2 代码

3.2.1 引用复制,直接比较引用

3.2.2 对象复制(浅复制,引用比较一定为false)

3.2.3 对象复制(深复制,引用比较一定为false)

3.3 小结

四、总结


 

一、概要

官方解释:Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。)

我的理解:最简单的理解,使用clone()代替new创建对象,减少性能消耗。要求是深复制,可以用clone()或者序列化实现。

参与者:抽象原型类AbstractPrototype、具体原型类ConcretePrototype、客户类Client

类图(Client直接访问ConcretePrototype):

设计模式(四)——原型模式_设计模式

类图(Client通过Factory类访问ConcretePrototype):

设计模式(四)——原型模式_设计模式_02

 

二、代码

代码(新建对象时使用原型模式)——与工厂模式类似的原型模式,原型模式的第一个优点,减少new关键字创建对象带来的性能消耗。

package mypackage;

import java.util.HashMap;
import java.util.Map;

public class TestPrototypePattern {

	public static void main(String[] args) {
		ShapeFactory _sShapeFactory = new ShapeFactory();
		_sShapeFactory.loadInit();
		for (int i = 0; i < 100; i++) {
			_sShapeFactory.get(i % 3 + 1).display();
		}

	}

}

abstract class Shape implements Cloneable {
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

	abstract void display();
}

class Circle extends Shape {

	@Override
	void display() {
		System.out.println("This is a Circle");
	}

}

class Rectangle extends Shape {

	@Override
	void display() {
		System.out.println("This is a Rectangle");

	}

}

class Square extends Shape {

	@Override
	void display() {
		System.out.println("This is a Square");

	}

}

class ShapeFactory {
	private Map<Integer, Shape> shapeList = new HashMap<>();

	public void loadInit() {
		shapeList.put(1, new Circle());
		shapeList.put(2, new Rectangle());
		shapeList.put(3, new Square());
	}

	public Shape get(int id) {
		Shape shape = shapeList.get(id);
		try {
			return (Shape) shape.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}

输出:

This is a Circle
This is a Rectangle
This is a Square
This is a Circle
This is a Rectangle
This is a Square
......

小结:在新建对象时使用原型模式优于工厂模式,可以减少new对象带来的性能消耗,对于包含引用的对象,浅复制达不到目的话,可以用clone()或者序列化实现深复制,对象属性值由客户端传入即可。

三、深复制和浅复制

3.1 概述

       Java中的复制包括引用复制和对象复制两种,其中对象复制又包括浅复制和深复制。

引用复制  
对象复制 浅复制
深复制

3.2 代码

3.2.1 引用复制,直接比较引用

直接复制引用—— Student student2 = student;

public class CopyTest {

	public static void main(String[] args) {
		Student student = new Student();
		Student student2 = student;
		System.out.println(student);
		System.out.println(student2);
		System.out.println(student == student2);
	}

}

class Student {

}

输出:

mypackage.Student@15db9742
mypackage.Student@15db9742
true

小结:引用复制,复制出来的引用和原引用指向同一个对象地址。

3.2.2 对象复制(浅复制,引用比较一定为false)

3.2.2.1 浅复制(实现Clonable接口重写clone()函数)

package mypackage;

public class CopyTest {

	public static void main(String[] args) throws Exception {
		Student student = new Student();
		student.setName("小明");
		student.set_age(18);
		Sex _sSex = new Sex();
		_sSex.setSex("男");
		student.setSex(_sSex);
		Address _dAddress = new Address();
		_dAddress.setProvince("广东省");
		_dAddress.setCity("广州市");
		student.set_address(_dAddress);

		System.out.println("修改前student: " + student);

		Student student2 = (Student) student.clone();
		student2.setName("小红");
		student2.set_age(20);
		
		student2.getSex().setSex("女");
		
		student2.get_address().setProvince("湖南省");
		student2.get_address().setCity("长沙市");

		System.out.println("修改后student: " + student);
		System.out.println("修改后student2: " + student2);
		System.out.println("引用是否相等: "+(student == student2));
	}

}

class Student implements Cloneable {
	private String name;
	private int _age;
	private Sex sex;
	private Address _address;

	public String getName() {
		return name;
	}

	public Sex getSex() {
		return sex;
	}

	public void setSex(Sex sex) {
		this.sex = sex;
	}

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

	public int get_age() {
		return _age;
	}

	public void set_age(int _age) {
		this._age = _age;
	}

	public Address get_address() {
		return _address;
	}

	public void set_address(Address _address) {
		this._address = _address;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", _age=" + _age + ", sex=" + sex + ", _address=" + _address + "]";
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

}

class Sex {
	private String sex;

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@Override
	public String toString() {
		return "Sex [sex=" + sex + "]";
	}

}

class Address {
	private String province;
	private String city;

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + "]";
	}

}

输出:

修改前student: Student [name=小明, _age=18, sex=Sex [sex=男], _address=Address [province=广东省, city=广州市]]
修改后student: Student [name=小明, _age=18, sex=Sex [sex=女], _address=Address [province=湖南省, city=长沙市]]
修改后student2: Student [name=小红, _age=20, sex=Sex [sex=女], _address=Address [province=湖南省, city=长沙市]]
引用是否相等: false

clone是Object自带的非final方法,默认是native,我们需要继承Clonable接口,然后才能重写clone()方法,这是对象复制(包括浅复制和深复制)的基础。

3.2.2.2 浅复制(使用构造函数实现浅复制)

package mypackage;

public class CopyTest {

	public static void main(String[] args) throws Exception {
		Student student = new Student();
		student.setName("小明");
		student.set_age(18);
		Sex _sSex = new Sex();
		_sSex.setSex("男");
		student.setSex(_sSex);
		Address _dAddress = new Address();
		_dAddress.setProvince("广东省");
		_dAddress.setCity("广州市");
		student.set_address(_dAddress);

		System.out.println("修改前student: " + student);

		Student student2 = new Student(student);
		student2.setName("小红");
		student2.set_age(20);
	

		
		student2.getSex().setSex("女");
		
		student2.get_address().setProvince("湖南省");
		student2.get_address().setCity("长沙市");
		
		
		System.out.println("修改后student: " + student);
		System.out.println("修改后student2: " + student2);
		System.out.println("引用是否相等: "+(student == student2));
	}

}

class Student  {
	private String name;
	private int _age;
	private Sex sex;
	private Address _address;

	public String getName() {
		return name;
	}

	public Sex getSex() {
		return sex;
	}

	public void setSex(Sex sex) {
		this.sex = sex;
	}

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

	public int get_age() {
		return _age;
	}

	public void set_age(int _age) {
		this._age = _age;
	}

	public Address get_address() {
		return _address;
	}

	public void set_address(Address _address) {
		this._address = _address;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", _age=" + _age + ", sex=" + sex + ", _address=" + _address + "]";
	}

	public Student(Student student) {
		this.name=student.name;
		this._age=student._age;
		this.sex=student.sex;
		this._address=student._address;
	}

	public Student() {
		super();
	}


}

class Sex {
	private String sex;

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@Override
	public String toString() {
		return "Sex [sex=" + sex + "]";
	}

}

class Address {
	private String province;
	private String city;

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + "]";
	}

}

输出:

修改前student: Student [name=小明, _age=18, sex=Sex [sex=男], _address=Address [province=广东省, city=广州市]]
修改后student: Student [name=小明, _age=18, sex=Sex [sex=女], _address=Address [province=湖南省, city=长沙市]]
修改后student2: Student [name=小红, _age=20, sex=Sex [sex=女], _address=Address [province=湖南省, city=长沙市]]
引用是否相等: false

小结:

1、浅复制实现的方式有两种:实现Clonable接口且使用clone()函数,以及在构造函数中浅复制。

2、浅复制特点:

对于基本类型数据,复制时使用值传递,因为传递过去的是值,所以副本修改后,原本不受影响;

对于引用类型数据,复制时使用引用传递,因为传递过去的是引用(即内存地址),实际上原本和副本指向同一对象,所以副本修改后,原本也要跟着修改。

3.2.3 对象复制(深复制,引用比较一定为false)

3.2.3.1 深复制(实现Clonable接口重写clone()方法)

package mypackage;

public class CopyTest {

	public static void main(String[] args) throws Exception {
		Student student = new Student();
		student.setName("小明");
		student.set_age(18);
		Sex _sSex = new Sex();
		_sSex.setSex("男");
		student.setSex(_sSex);
		Address _dAddress = new Address();
		_dAddress.setProvince("广东省");
		_dAddress.setCity("广州市");
		student.set_address(_dAddress);

		System.out.println("修改前student: " + student);

		Student student2 = (Student) student.clone();
		student2.setName("小红");
		student2.set_age(20);


		student2.getSex().setSex("女");


		student2.get_address().setProvince("湖南省");
		student2.get_address().setCity("长沙市");

		System.out.println("修改后student: " + student);
		System.out.println("修改后student2: " + student2);
		System.out.println("引用是否相等: "+(student == student2));
	}

}

class Student implements Cloneable {
	private String name;
	private int _age;
	private Sex sex;
	private Address _address;

	public String getName() {
		return name;
	}

	public Sex getSex() {
		return sex;
	}

	public void setSex(Sex sex) {
		this.sex = sex;
	}

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

	public int get_age() {
		return _age;
	}

	public void set_age(int _age) {
		this._age = _age;
	}

	public Address get_address() {
		return _address;
	}

	public void set_address(Address _address) {
		this._address = _address;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", _age=" + _age + ", sex=" + sex + ", _address=" + _address + "]";
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		Student _newStudent = (Student) super.clone();
		_newStudent.sex = (Sex) sex.clone();
		_newStudent._address = (Address) _address.clone();
		return _newStudent;
	}

}

class Sex implements Cloneable {
	private String sex;

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@Override
	public String toString() {
		return "Sex [sex=" + sex + "]";
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

class Address implements Cloneable {
	private String province;
	private String city;

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + "]";
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

}

输出:

修改前student: Student [name=小明, _age=18, sex=Sex [sex=男], _address=Address [province=广东省, city=广州市]]
修改后student: Student [name=小明, _age=18, sex=Sex [sex=男], _address=Address [province=广东省, city=广州市]]
修改后student2: Student [name=小红, _age=20, sex=Sex [sex=女], _address=Address [province=湖南省, city=长沙市]]
引用是否相等: false

3.2.3.2 深复制(序列化实现深复制)

如果原本中注入了类的对象,你就不得不去每个引用到的对象去复写这个clone方法,非常繁琐,这里提供另外一个深复制的方法,序列化实现深复制。

package mypackage;

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

public class CopyTest {

	public static void main(String[] args) throws Exception {
		Student student = new Student();
		student.setName("小明");
		student.set_age(18);
		Sex _sSex = new Sex();
		_sSex.setSex("男");
		student.setSex(_sSex);
		Address _dAddress = new Address();
		_dAddress.setProvince("广东省");
		_dAddress.setCity("广州市");
		student.set_address(_dAddress);

		System.out.println("修改前student: " + student);

		Student student2 = (Student) student.deepCopy();
		student2.setName("小红");
		student2.set_age(20);

		student2.getSex().setSex("女");


		student2.get_address().setProvince("湖南省");
		student2.get_address().setCity("长沙市");

		System.out.println("修改后student: " + student);
		System.out.println("修改后student2: " + student2);
		System.out.println("引用是否相等: "+(student == student2));
	}

}

class Student implements Serializable {
	private static final long serialVersionUID = 1L;
	private String name;
	private int _age;
	private Sex sex;
	private Address _address;

	public String getName() {
		return name;
	}

	public Sex getSex() {
		return sex;
	}

	public void setSex(Sex sex) {
		this.sex = sex;
	}

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

	public int get_age() {
		return _age;
	}

	public void set_age(int _age) {
		this._age = _age;
	}

	public Address get_address() {
		return _address;
	}

	public void set_address(Address _address) {
		this._address = _address;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", _age=" + _age + ", sex=" + sex + ", _address=" + _address + "]";
	}

	public Object deepCopy() throws Exception {
		ByteArrayOutputStream _bByteArrayOutputStream = new ByteArrayOutputStream();
		ObjectOutputStream _bObjectOutputStream = new ObjectOutputStream(_bByteArrayOutputStream);
		_bObjectOutputStream.writeObject(this);

		ByteArrayInputStream _bByteArrayInputStream = new ByteArrayInputStream(_bByteArrayOutputStream.toByteArray());
		ObjectInputStream _bObjectInputStream = new ObjectInputStream(_bByteArrayInputStream);
		return _bObjectInputStream.readObject();

	}

}

class Sex implements Serializable {

	private static final long serialVersionUID = 1L;
	private String sex;

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@Override
	public String toString() {
		return "Sex [sex=" + sex + "]";
	}

}

class Address implements Serializable {
	private static final long serialVersionUID = 1L;
	private String province;
	private String city;

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + "]";
	}

}

输出:

修改前student: Student [name=小明, _age=18, sex=Sex [sex=男], _address=Address [province=广东省, city=广州市]]
修改后student: Student [name=小明, _age=18, sex=Sex [sex=男], _address=Address [province=广东省, city=广州市]]
修改后student2: Student [name=小红, _age=20, sex=Sex [sex=女], _address=Address [province=湖南省, city=长沙市]]
引用是否相等: false

小结:

1、深复制的两种实现方式:clone()方法和序列化

2、深复制特点: 无论是基本类型还是引用类型,都已经完全的在内存中新建了一块内容,所以当副本修改时,原本不会受到影响,反之原本修改,副本亦不会受到影响。

3.3 小结

无论是引用复制、浅复制还是深复制,都是Java中一项基本语法,熟练掌握Java基本语法为后续Java学习打下良好的基础。

四、总结

原型模式适用情形:

  1.在需要一个类的大量对象的时候,使用原型模式是最佳选择,因为原型模式是在内存中对这个对象进行拷贝,要比直接new这个对象性能要好很多,在这种情况下,需要的对象越多,原型模式体现出的优点越明显。

  2.如果一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,那么可以使用原型模式。

  3.当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。

 

上一篇:设计模式(三)——单例模式

下一篇:设计模式(五)——建造者模式