提出问题
我们需要创建大量的 Phone 对象,并且还要对较多 成员变量赋值,而且 值相同
Phone
public class Phone{
private String name;
private String cpu;
private int ram;
private int rom;
private float screen;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getRam() {
return ram;
}
public void setRam(int ram) {
this.ram = ram;
}
public int getRom() {
return rom;
}
public void setRom(int rom) {
this.rom = rom;
}
public float getScreen() {
return screen;
}
public void setScreen(float screen) {
this.screen = screen;
}
}
测试类
public class Test {
public static void main(String[] args) {
Phone phone=new Phone();
phone.setName("华为");
phone.setCpu("海思麒麟");
phone.setRam(12);
phone.setRom(256);
phone.setScreen(6.2f);
//每次创建成员变量相同的对象,都需要设置成员变量,而且成员变量的值是相同的
Phone phone2=new Phone();
phone2.setName("华为");
phone2.setCpu("海思麒麟");
phone2.setRam(12);
phone2.setRom(256);
phone2.setScreen(6.2f);
}
}
问题总结
大量创建 成员变量相同 的对象,都需要给成员变量赋值,而且成员变量的值是相同的,非常麻烦
原型模式
根据一个对象,克隆一个新对象,该新对象的成员变量 与 原对象 相同,而且 克隆的方法非常简单
应用场景
- 简化创建对象。尤其是频繁创建对象,需要对较多成员变量赋值,而且 成员变量相同
- 克隆一个对象,比用
new创建对象,效率更高。因为Object类的clone方法是一个本地方法,它直接 操作内存,特别是复制大对象时,性能的差别非常明显
真实应用
spring框架的原型模式
实现
实现 Cloneable 接口
在运行时通知虚拟机可以使用 clone 方法
只有实现了这个接口的类才可以被克隆,否则抛出 CloneNotSupportedException 异常。
重写 Object 类中的 clone() 方法
Object 类中有 clone 方法,作用是返回对象的一个克隆
但是 protected 访问权限,一般的类无法调用,实现类需要将 clone方法修改为 public 类型
案例-原型模式
Phone2
让类实现 Cloneable 接口,并重写 clone() 方法
class Phone2 implements Cloneable{
private String name;
private String cpu;
private int ram;
private int rom;
private float screen;
@Override
public Phone2 clone() {
Phone2 ret=null;
try {
ret = (Phone2)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return ret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getRam() {
return ram;
}
public void setRam(int ram) {
this.ram = ram;
}
public int getRom() {
return rom;
}
public void setRom(int rom) {
this.rom = rom;
}
public float getScreen() {
return screen;
}
public void setScreen(float screen) {
this.screen = screen;
}
}
测试类
public class Test2 {
public static void main(String[] args) {
Phone2 phone=new Phone2();
phone.setName("华为");
phone.setCpu("海思麒麟");
phone.setRam(12);
phone.setRom(256);
phone.setScreen(6.2f);
//再次创建成员变量相同的对象,可以克隆
Phone2 phone2=phone.clone();
System.out.println(phone2.getName());
}
}
实现原理
克隆对象 不会 调用类的 构造方法。
克隆是通过调用 Object 类的 clone 方法来完成的,它直接 在内存中复制数据,因此不会调用到类的构造方法。
private 构造方法
private 构造方法,也不会影响克隆,因为 克隆 不调用构造方法,所以,单例模式与原型模式是冲突的,在使用时要特别注意。
验证
Phone类
class Phone3 implements Cloneable{
private String name;
private String cpu;
private int ram;
private int rom;
private float screen;
public Phone3() {
System.out.println("测试克隆调用构造方法!!!");
}
@Override
public Phone3 clone() {
Phone3 ret=null;
try {
ret = (Phone3)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return ret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getRam() {
return ram;
}
public void setRam(int ram) {
this.ram = ram;
}
public int getRom() {
return rom;
}
public void setRom(int rom) {
this.rom = rom;
}
public float getScreen() {
return screen;
}
public void setScreen(float screen) {
this.screen = screen;
}
}
测试类
public class 原型模式3 {
public static void main(String[] args) {
Phone3 phone=new Phone3();
phone.setName("华为");
phone.setCpu("海思麒麟");
phone.setRam(12);
phone.setRom(256);
phone.setScreen(6.2f);
//再次创建成员变量相同的对象,可以克隆
Phone3 phone2=phone.clone();
System.out.println(phone2.getName());
}
}
执行结果:
测试克隆调用构造方法!!!
总结
只有 new 的时候才会调用构造方法,克隆 不会调用构造方法
浅拷贝
Object类的 clone 方法只会 克隆 对象中的 基本数据类型 和 包装类
对于 数组、对象 等都不会拷贝,这就是浅拷贝
如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝
案例
phone类:
class Phone4 implements Cloneable{
private String name;
//电话号码
private List<String> numberList =new ArrayList<>();
@Override
public Phone4 clone() {
Phone4 ret=null;
try {
ret = (Phone4)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return ret;
}
public void addNumber(String number){
numberList.add(number);
}
public List<String> getNumberList() {
return numberList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类
public class Test {
public static void main(String[] args) {
Phone4 lileiPhone=new Phone4();
lileiPhone.setName("李雷的大宝剑");
//给 李雷的phone 添加电话号码
lileiPhone.addNumber("137");
lileiPhone.addNumber("138");
lileiPhone.addNumber("139");
//再次创建成员变量相同的对象,可以克隆
Phone4 lucyPhone=lileiPhone.clone();
lucyPhone.setName("lucy的手机");
//给lucy的phone添加电话号码
lucyPhone.addNumber("110");
//修改lucy的phone中的name,不会干扰李雷的phone,因为是基本数据类型 或字符串
System.out.println(lileiPhone.getName());
//遍历打印李雷的phone的电话号码,居然打印除了110,说明成员变量是对象类型时,是共享的,会互相干扰
for(String item:lileiPhone.getNumberList()){
System.out.println(item);
}
}
}
执行结果:
李雷的大宝剑
137
138
139
110
总结
根据 李雷的phone,克隆 lucy的phone,修改 lucy的phone的 name (字符串类型),不会干扰 李雷的phone
修改 lucy的phone的 其他对象(除了 基本数据类型、字符串以外),会干扰 李雷的phone
深拷贝
深拷贝可以解决上面的问题,克隆后,还要将 对象类型 的成员变量克隆
注意: 成员 是 基本数据类型、字符串 时,不需要做此处理
案例
phone类:
class Phone5 implements Cloneable{
private String name;
//电话号码
private List<String> numberList =new ArrayList<>();
@Override
public Phone5 clone() {
Phone5 ret=null;
try {
ret = (Phone5)super.clone();
//当成员变量的类型不是 基本数据类型、字符串时,就重新创建对象 或 克隆(数组可直接克隆)
ret.numberList = new ArrayList<>();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return ret;
}
public void addNumber(String number){
numberList.add(number);
}
public List<String> getNumberList() {
return numberList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类
public class 原型模式_深拷贝 {
public static void main(String[] args) {
Phone5 lileiPhone=new Phone5();
lileiPhone.setName("李雷的大宝剑");
//给 李雷的phone 添加电话号码
lileiPhone.addNumber("137");
lileiPhone.addNumber("138");
lileiPhone.addNumber("139");
//再次创建成员变量相同的对象,可以克隆
Phone5 lucyPhone=lileiPhone.clone();
lucyPhone.setName("lucy的手机");
//给lucy的phone添加电话号码
lucyPhone.addNumber("110");
System.out.println("lilei phone=================");
//修改lucy的phone中的name,不会干扰李雷的phone,因为是基本数据类型 或字符串
System.out.println(lileiPhone.getName());
//遍历打印李雷的phone的电话号码,居然打印除了110,说明成员变量是对象类型时,是共享的,会互相干扰
for(String item:lileiPhone.getNumberList()){
System.out.println(item);
}
System.out.println("lucy phone=================");
for(String item:lucyPhone.getNumberList()){
System.out.println(item);
}
}
}
执行结果:
lilei phone=================
李雷的大宝剑
137
138
139
lucy phone=================
110
总结
深克隆后,2个对象的 List 就不会互相干扰
List不支持 克隆,只能创建
如果 成员变量 是 数组、自定类(且实现 Cloneable 接口),可 直接克隆