☰
原型模式详解:克隆对象与浅克隆/深克隆
2026/10/1 14:41:54 网站建设 项目流程

一、原型模式定义

原型模式采用复制原型对象的方法来创建实例。使用原型模式创建出来的对象,具有与原型一样的数据结构,也拥有与原型一样的值。

核心思想:对象创建的动作发自原型对象自身(clone()),而不是由外部new出来。


二、原型模式的特点

  1. 由原型对象自身创建目标对象:即"对象创建"这一动作来自原型对象本身的clone()方法。
  2. 目标对象是原型的一个克隆:通过原型模式创建的对象,不仅与原型有相同的结构,还有相同的值。
  3. 根据克隆深度的不同,分浅度克隆与深度克隆(见第四节)。

三、基础代码实现

Object类自带protected Object clone(),但只有实现了Cloneable标记接口的类,调用clone()才不会抛CloneNotSupportedException。

// 必须实现 Cloneable 接口才能克隆publicclassPersonimplementsCloneable{privateStringname;privateStringage;privateStringsex;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetAge(){returnage;}publicvoidsetAge(Stringage){this.age=age;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}// 克隆方法:重写 Object.clone(),提升为 public@OverridepublicPersonclone(){try{return(Person)super.clone();// 默认是【浅克隆】}catch(CloneNotSupportedExceptione){e.printStackTrace();returnnull;}}}publicclassMainClass{publicstaticvoidmain(String[]args){Personperson1=newPerson();person1.setName("李白");person1.setAge("34");person1.setSex("男");// 克隆对象(堆里生成了一份新对象)Personperson2=person1.clone();System.out.println(person2.getName());// 李白(与原型数据一致)}}

⚠️super.clone()默认执行的是浅克隆(见下节)。这里的字段都是String(不可变)和基本类型,浅克隆看不出副作用。


四、浅克隆 vs 深克隆(核心)

4.1 一个关键澄清

super.clone()的本质是:逐字段复制。

  • 基本类型(int、double…):直接复制值,互不干扰;
  • 引用类型(对象、数组…):只复制引用地址,新旧对象指向同一个引用对象。

原笔记说"数值对象会生成全新一份"并不准确——String是引用类型,只是因为它不可变,即使浅克隆共享了引用,重新赋值p.name="x"也只是让p指向新字符串,不会改动原对象,所以"看起来"没副作用。要真正暴露浅克隆问题,必须用可变引用对象。

4.2 浅克隆的副作用(用可变对象演示)

给Person增加一个可变的引用属性Address:

publicclassAddressimplementsCloneable{privateStringcity;publicAddress(Stringcity){this.city=city;}publicStringgetCity(){returncity;}publicvoidsetCity(Stringcity){this.city=city;}@OverridepublicAddressclone(){try{return(Address)super.clone();}catch(CloneNotSupportedExceptione){returnnull;}}}publicclassPersonimplementsCloneable{privateStringname;privateAddressaddress;// 可变引用对象// 浅克隆:address 引用被复制,p1、p2 指向同一个 Address@OverridepublicPersonclone(){try{return(Person)super.clone();}catch(CloneNotSupportedExceptione){returnnull;}}}publicclassMainClass{publicstaticvoidmain(String[]args){Personp1=newPerson("李白",newAddress("长安"));Personp2=p1.clone();// 浅克隆p2.getAddress().setCity("洛阳");// 改 p2 的地址System.out.println(p1.getAddress().getCity());// 输出"洛阳"!p1 被牵连}}

结论:浅克隆下,p1和p2共享同一个Address,改一个另一个跟着变——这通常不是我们想要的。

4.3 深克隆的修法

深克隆 = 在clone()里连引用对象也一起克隆:

publicclassPersonimplementsCloneable{privateStringname;privateAddressaddress;@OverridepublicPersonclone(){try{Personp=(Person)super.clone();p.address=this.address.clone();// 引用对象也克隆一份returnp;}catch(CloneNotSupportedExceptione){returnnull;}}}

此时p2.getAddress().setCity("洛阳")不会影响p1,二者完全独立。

4.4 深克隆的另一种方式:序列化

当对象层级很深时,逐层clone()很繁琐。可让类实现Serializable,通过序列化成字节流再反序列化得到完全独立的新对象(要求所有引用对象都可序列化):

// 伪代码思路:把对象写进 ByteArrayOutputStream,再读回 → 全新深拷贝

面试常问:深克隆两种实现——逐层 clone()或序列化。前者直观但层次深时繁琐,后者"一行深拷贝"但要求全链路可序列化。


五、角色与职责

角色职责
抽象原型(Prototype)声明克隆自身的接口。Java 中即Cloneable标记接口 +clone()方法
具体原型(Concrete Prototype)实现clone(),返回自身的一个副本
客户(Client)通过调用原型的clone()创建新对象,而不使用new

六、应用场景

  • 创建对象时,希望新对象既继承基类结构,又继承原型的数据:直接clone比从头new再逐个set更高效。
  • 希望修改目标对象不影响既有原型:配合深克隆,克隆体与原型完全隔离。
  • 隐藏克隆细节:对象本身的克隆往往涉及类内部数据细节,用原型模式把这些细节封装在clone()内,调用方无感知。
  • 对象创建成本高 / 构造复杂:如从数据库或网络加载后缓存为原型,后续直接复制,省去昂贵初始化。

七、总结

  • 原型模式 =复制现有对象来创建新对象,而非new。
  • 浅克隆:复制基本类型值 + 复制引用地址(引用对象共享);深克隆:引用对象也一并克隆(完全独立)。
  • String不可变,单用String字段无法体现浅克隆问题,必须用可变引用对象演示。
  • 记忆:浅克隆共享引用、深克隆独立副本;层次深时用序列化做深克隆更省事。

一句话总结:原型模式用clone()拷贝代替new,省去重复初始化;用之前先想清楚——你的对象里有没有可变引用?有,就老老实实做深克隆,否则改一个坑一片。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询