生命之灯因热情而点燃,生命之舟因拼搏而前行!
愿自己能在自己所热爱的道路上越走越远。
今天我们以创建对象的几种方式来探讨Java设计模式之原型模式.
Java创建对象对象除了new关键字和反射之外你还知道其他创建对象的方法吗?
在这里为了防止有人只知道new关键字创建对象,这里先说一下反射创建对象.
- 反射创建对象
// 1、用反射创建对象
try {
// 使用反射的该方法需要改类有一个空的构造器否则将会报错.
Student o = (Student) Class.forName("com.mzx.gulimall.product.web.Student").newInstance();
// 利用要创建对象的类获取该类中的构造器对象,通过构造器对象来构造该对象,该用法省略了类型强制转换,比较安全.
// 该用法实际上就是调用该类的构造器来进行初始化.
// 每一个方法都有一个唯一的方法签名,就算是构造方法也不例外, 而这里通过参数类型来获取的类构造器就是获取方法签名.
// 从而保证了正确的调用正确的构造器.
// 由于确定了要使用的构造器,所以说通过获取构造器来创建对象不需要默认的空构造器.
Student student = Student.class.getConstructor(String.class).newInstance("我是张三");
} catch (Exception e) {
e.printStackTrace();
}
- 序列化创建对象
package com.mzx.gulimall.product.web;
import java.io.*;
/**
* @author ZhenXinMa
* @date 2020/8/10 14:15
*/
public class Student implements Serializable,Cloneable {
private String name;
public Student(String name) {
this.name = name;
}
public Student(){}
@Override
protected Student clone() throws CloneNotSupportedException {
// 序列化.
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
// 反序列化
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Student) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
通过序列化创建的对象就相当于在内存中新开辟一个新的空间来存放对象,而不是将所有的引用都指向同一个内存空间.
测试通过序列化创建对象的hashCode值.
public static void main(String[] args) throws Exception {
Student student = new Student("张三");
Student clone = student.clone();
// 其输出结果是false.
System.out.println(student.hashCode() == clone.hashCode());
}
- 使用Object类的clone方法来创建对象属于浅拷贝,下面介绍原型模式的时候会详细介绍浅拷贝和深拷贝. 所以在这里就不详细的介绍clone方法了
好了,步入正题了,我们开始了解原型模式了
原型模式的初衷就是未了方便快速的创建出一个和原先对象一模一样的对象,并且可以通过自定义配置其新创建的对象在内存中是否和原先对象有同一个内存空间.
我们先来了解下浅拷贝和深拷贝.
- 浅拷贝
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新对象.
- 对于数据类型是引用类型的成员变量,比如说成员变量是某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(该对象的内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个内存空间。在这种情况下,在一个对象中修改成员变量会影响到另一个对象的该成员变量。
- 实现浅拷贝很简单,那就是使用默认的Object的clone方法。
- 深拷贝
- 复制对象的所有基本数据类型的成员变量值
- 为所有引用数据类型的成员变量申请内存空间,并复制每个引用类型成员变量所引用的对象,知道该对象可达到所有对象。也就是说,对象进行深拷贝要对整个对象记性拷贝.
- 实现方式为重写clone方法或者序列化.
归根到底Java设计模式之原型模式其实就是对clone的另一种名词上的阐述. 原型模式本质上就是通过clone来创建和原先对象一模一样的对象的,只是对该方法进行了一些自定义的配置而已.
现在我们假设有一个People,该类有一个name属性和Friend属性。 还有一个Ffiend类该类有name和age两个属性. 通过浅拷贝实现的原型模式就类似于Spring里面的单例模式,这里就不做过多的叙述,因为仅仅只是调用了Object的clone方法. 我们重点讲下原型模式下面的深拷贝.
看代码.
package com.mzx.base.designmodel.clone;
import java.io.*;
/**
* @author ZhenXinMa
* @date 2020/8/10 13:51
*/
public class People implements Cloneable, Serializable {
private String name;
private Friend friend;
public People(String name, Friend friend) {
this.name = name;
this.friend = friend;
}
public People() {
}
/**
* 通过序列化获取对象,就相当于在堆中开辟空间存放一个新的对象,而不是将不同的引用指向了相同的内存空间.
* @return
* @throws CloneNotSupportedException
*/
@Override
protected People clone() throws CloneNotSupportedException {
try {
// 序列化.
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
// 将当前对象写入字节数组输出流中.
objectOutputStream.writeObject(this);
// 在通过反序列化来获取对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
/*
* --------------------------------------------------------
* 这里通过序列化实现了原型模式下的深拷贝,通过该方法获取的实例将会在JVM
* 管理的内存空间上重新划分出一个新的地址来存放该对象,而不是将新对象的
* 引用指针指向老对象的内存空间地址上.
* 如果要实现浅拷贝(引用传值)将旧对象引用的值赋值给新的对象,使其和
* 旧对象共同指向一个内存空间上,这个就和Spring的单例模式很像. 而如果要
* 实现浅拷贝,那么只需要实现默认的clone方法即可. 只需要在clone方法内
* 加上一句: return super.clone(); 即可实现原型模式下的浅拷贝.
* --------------------------------------------------------
* */
// 从字节数组输入流中读取对象.
return (People) objectInputStream.readObject();
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
}