- 示例代码:
package com.pattern.demo01\_类的拷贝;
import java.lang.reflect.Constructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo02\_使用反射来创建对象 {
public static void main(String[] args) throws Exception {
// 获取类的字节码对象
Class<Resume> resumeClass = Resume.class;
// 获取空参构造方法
Constructor<Resume> resumeConstructor = resumeClass.getDeclaredConstructor(String.class, Integer.class,String.class,String.class);
Resume r1 = resumeConstructor.newInstance("小明", 20,"本科","3年");
Resume r2 = resumeConstructor.newInstance("小明", 20,"本科","3年");
Resume r3 = resumeConstructor.newInstance("小明", 20,"本科","3年");
System.out.println(r1.hashCode()); // 21685669(没有重写的hashCode默认根据内存地址值来计算)
System.out.println(r2.hashCode()); // 2133927002
System.out.println(r3.hashCode()); // 1836019240
}
}
可见,不管是基于new的方式创建对象,还是基于反射来创建对象都是比较繁琐的;
5.2.3 clone方法
在JDK中,提供有clone()方法来帮助我们对一个对象进行克隆操作,该对象必须实现Cloneable接口,代表该对象是可以被克隆的,否则将被抛出CloneNotSupportedException
异常。clone()方法被native修饰;
- 修改Resumer类,重写clone()方法:
package com.pattern.demo01\_类的拷贝;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
public class Resume implements Cloneable { // 被克隆的类必须要实现Cloneable接口
private String name;
private Integer age;
private String education;
private String experience;
public void show() {
System.out.println("姓名【" + name + "】,年龄【" + age + "】,学历【" + education + "】,经验【" + experience + "】");
System.out.println("---------------");
}
/\*\*
\* 重写Object类完成对对象的克隆
\*
\* @return
\* @throws CloneNotSupportedException
\*/
@Override
public Resume clone() throws CloneNotSupportedException {
return (Resume) super.clone();
}
}
- 使用clone方法复制三份简历:
package com.pattern.demo01\_类的拷贝;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo03\_使用clone方法 {
public static void main(String[] args) throws Exception {
Resume r1 = new Resume("小明", 20, "本科", "3年");
Resume r2 = r1.clone();
Resume r3 = r1.clone();
r1.show();
r2.show();
r3.show();
System.out.println(r1 == r2); // false
System.out.println(r1 == r3); // false
System.out.println(r2 == r3); // false
}
}
运行结果如下:
需要注意的是:clone方法的本质是克隆,在堆内存中开辟了新的内存空间;就好比如每一份简历都是独立的存在;不存在第一份简历的内容上写着"内容在第二份简历上"
另外,clone方法是直接从堆内存中以二进制流的方式进行复制,并且重新分配一个内存块,因此效率非常高。由于clone方法基于内存复制,因此不会调用对象的构造函数,也就是不会经过对象的初始化过程;
- 修改类:
package com.pattern.demo01\_类的拷贝;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Resume implements Cloneable { // 被克隆的类必须要实现Cloneable接口
private String name;
private Integer age;
private String education;
private String experience;
public Resume() {
System.out.println("Resume()...");
}
public Resume(String name, Integer age, String education, String experience) {
System.out.println("Resume(String name, Integer age, String education, String experience)...");
this.name = name;
this.age = age;
this.education = education;
this.experience = experience;
}
public void show() {
System.out.println("姓名【" + name + "】,年龄【" + age + "】,学历【" + education + "】,经验【" + experience + "】");
System.out.println("---------------");
}
/\*\*
\* 重写Object类完成对对象的克隆
\*
\* @return
\* @throws CloneNotSupportedException
\*/
@Override
public Resume clone() throws CloneNotSupportedException {
return (Resume) super.clone();
}
}
- 示例代码:
package com.pattern.demo01\_类的拷贝;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo04\_clone不是使用构造方法来创建对象 {
public static void main(String[] args) throws Exception {
// 执行有参构造方法
Resume r1 = new Resume("小明", 20, "本科", "3年");
// 底层并不是通过构造方法来创建对象(不会执行任何的构造方法)
Resume r2 = r1.clone();
Resume r3 = r1.clone();
}
}
5.2 深克隆与浅克隆
在克隆对象时分为深克隆与浅克隆:
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Object中的clone方法就是采用浅克隆;
5.2.1 浅克隆
- 定义一个Person对象:
package com.pattern.demo02\_浅克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person implements Cloneable{
private String name;
private Integer age;
private Child child;
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
- 定义一个Child对象:
package com.pattern.demo02\_浅克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Child {
private String name;
private Integer age;
}
- 测试浅克隆:
package com.pattern.demo02\_浅克隆;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo01\_测试浅克隆 {
public static void main(String[] args) throws Exception {
Person person = new Person("王刚", 20, new Child("王铁", 5));
Person clone = person.clone();
// 对应引用数据类型,都是引用同一块内存地址值
System.out.println(person.getChild() == clone.getChild()); // true
System.out.println("----------------");
System.out.println(person.getName());
System.out.println(person.getAge());
System.out.println(person.getChild().getName());
System.out.println(person.getChild().getAge());
System.out.println("-----------------------------");
clone.setName("李刚");
clone.setAge(30);
Child child = clone.getChild();
/\*
对于引用数据类型(String除外),clone对象和person对象指向的是同一块内存地址值
\*/
child.setName("李铁");
child.setAge(8);
System.out.println(person.getName()); // 王刚
System.out.println(person.getAge()); // 20
System.out.println(person.getChild().getName()); // 李铁
System.out.println(person.getChild().getAge()); // 8
System.out.println("-----------------------------");
}
}
运行结果:
5.2.2 深克隆
1)clone实现深克隆
深克隆的目的是对于对象中的每个引用数据类型都需要克隆一份新的数据;这样修改克隆出来的对象时不会影响原型对象;
- 修改Child对象:
package com.pattern.demo03\_深克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Child implements Cloneable {
private String name;
private Integer age;
@Override
public Child clone() throws CloneNotSupportedException {
return (Child) super.clone();
}
}
- 修改Person对象:
package com.pattern.demo03\_深克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person implements Cloneable{
private String name;
private Integer age;
private Child child;
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
// 为每一个person对象都单独克隆一份child对象
person.setChild(child.clone());
return person;
}
}
- 测试类:
package com.pattern.demo03\_深克隆;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo01\_测试深克隆 {
public static void main(String[] args) throws Exception {
Person person = new Person("王刚", 20, new Child("王铁", 5));
Person clone = person.clone();
// 对应引用数据类型,都是引用同一块内存地址值
System.out.println(person.getChild() == clone.getChild()); // true
System.out.println("----------------");
System.out.println(person.getName());
System.out.println(person.getAge());
System.out.println(person.getChild().getName());
System.out.println(person.getChild().getAge());
System.out.println("-----------------------------");
clone.setName("李刚");
clone.setAge(30);
Child child = clone.getChild();
// 修改clone对象中的Child对象内容不会对原型对象中的Child对有影响
child.setName("李铁");
child.setAge(8);
System.out.println(person.getName()); // 王刚
System.out.println(person.getAge()); // 20
System.out.println(person.getChild().getName()); // 李铁
System.out.println(person.getChild().getAge()); // 8
System.out.println("-----------------------------");
}
}
运行结果:
使用clone()方法来完成深克隆会比较麻烦,如果Child中还存在一个Gradnson类,那么同样需要在Child的clone方法中设置Grandson类;
- 添加Grandson:
package com.pattern.demo03\_深克隆;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
public class Grandson implements Cloneable{
private String name;
private Integer age;
@Override
protected Grandson clone() throws CloneNotSupportedException {
return (Grandson) super.clone();
}
}
- 修改Child:
package com.pattern.demo03\_深克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Child implements Cloneable {
private String name;
private Integer age;
private Grandson grandson;
@Override
public Child clone() throws CloneNotSupportedException {
Child child = (Child) super.clone();
child.setGrandson(grandson.clone());
return child;
}
}
- 测试代码:
package com.pattern.demo03\_深克隆;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo01\_测试深克隆 {
public static void main(String[] args) throws Exception {
Person person = new Person("王刚", 20, new Child("王铁", 5,new Grandson("王铜",1)));
Person clone = person.clone();
Child child = person.getChild();
Grandson grandson = child.getGrandson();
Child cloneChild = clone.getChild();
Grandson cloneChildGrandson = cloneChild.getGrandson();
System.out.println(child == cloneChild); // false
System.out.println(grandson == cloneChildGrandson); // false
}
}
2)序列化实现深克隆
- Grandson:
package com.pattern.demo04\_序列化实现深克隆;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
public class Grandson implements Serializable { // 需要实现序列化接口
private String name;
private Integer age;
}
- Child:
package com.pattern.demo04\_序列化实现深克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Child implements Serializable { // 需要实现序列化接口
private String name;
private Integer age;
private Grandson grandson;
}
- Person:
package com.pattern.demo04\_序列化实现深克隆;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person implements Serializable { // 需要实现序列化接口
private String name;
private Integer age;
private Child child;
}
- 测试代码:
package com.pattern.demo04\_序列化实现深克隆;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo01\_测试序列化实现深克隆 {
public static void main(String[] args) throws Exception {
Person person = new Person("王刚", 20, new Child("王铁", 5, new Grandson("王铜", 1)));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
// 把对象序列化到内存中
oos.writeObject(person);
// 从内存中反序列化出来
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Person clone = (Person) ois.readObject();
Child child = person.getChild();
Grandson grandson = child.getGrandson();
Child cloneChild = clone.getChild();
Grandson cloneChildGrandson = cloneChild.getGrandson();
System.out.println(child == cloneChild); // false
System.out.println(grandson == cloneChildGrandson); // false
}
}
5.3 原型设计模式的应用
- 简历类:
package com.pattern.demo05\_原型设计模式的应用;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Resume implements Cloneable,Serializable {
// 姓名
private String name;
// 年龄
private Integer age;
// 学历
private String education;
// 工作经历
private WorkExperience workExperience;
public void show() {
System.out.println(
"姓名【" + name + "】," +
"年龄【" + age + "】," +
"学历【" + education + "】," +
"公司【" + workExperience.getCompany() + "】," +
"薪资【" + workExperience.getSalary() + "】");
}
@Override
protected Resume clone() throws CloneNotSupportedException {
return (Resume) super.clone();
}
}
- 工作经历类:
package com.pattern.demo05\_原型设计模式的应用;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class WorkExperience implements Serializable {
private String company;
private String salary;
}
- 浅克隆:
package com.pattern.demo05\_原型设计模式的应用;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo01\_原型设计模式的应用_浅克隆 {
public static void main(String[] args) throws Exception {
Resume r1 = new Resume("小明", 25, "本科", new WorkExperience("京东", "12K"));
r1.show();
Resume r2 = r1.clone();
r2.show();
}
}
- 深克隆:
package com.pattern.demo05\_原型设计模式的应用;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class Demo02\_原型设计模式的应用_深克隆 {
public static void main(String[] args) throws Exception {
Resume r1 = new Resume("小明", 25, "本科", new WorkExperience("京东", "12K"));
r1.show();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
// 把对象序列化到内存中
oos.writeObject(r1);
// 从内存中反序列化出来
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Resume r2 = (Resume) ois.readObject();
r2.show();
}
}
5.4 单例与原型设计模式
我们之前学习过单例设计模式,即在JVM进程中只会存在一个实例对象,如果复制的目标对象恰好是单例对象,那会不会破坏单例对象呢?当然,实际情况下我们肯定不会把单例类使用原型设计模式来拷贝;我们来测试一下单例类与原型设计模式,试看是否会破坏单例模式;
- 示例代码:
package com.pattern.demo06\_单例与原型;
/\*\*
\* @author lscl
\* @version 1.0
\* @intro:
\*/
public class TestObj implements Cloneable {