
通过 new 关键字生成实例是需要指定类名的。在开发过程中,我们需要「在不指定类名的前提下生成实例」的需求。
在以下情况,我们就不能根据类来生成实例,而要根据现有的实例来生成新的实例。

原型模式:
clone 创建出实例的副本。clone() 拷贝已有对象的数据,更新少量差值来实现。原型模式的基本工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象复制原型自己来实现创建过程。

登场角色:
Prototype(抽象原型类):定义用于复制现有实例来生成新实例的方法,可以是抽象类也可以是接口。在 Java 中,这个抽象类是实现了 @Cloneable 接口的。ConcretePrototype(具体原型类):实现复制现有实例并生成新实例的方法。除了将原始对象的数据复制到克隆体中之外,该方法有时还需处理克隆过程中的极端情况,例如克隆关联对象和梳理递归依赖等等。Client(使用者):使用复制实例的方法生成新实例。| 优点 | 缺点 |
|---|---|
| 解耦。克隆对象释放无需与它们所属的类相耦合。 | 克隆包含循环引用的复杂对象可能会非常麻烦。 |
| 克隆预生成原型,避免反复运行初始化代码。 | 需要为每一个类配备一个克隆方法,因此对已有类进行改造比较麻烦。需要修改其源代码,并且在实现深克隆时需要编写较为复杂的代码。 |
| 方便生成复杂对象。 | |
| 用继承以外的方式来处理复杂对象的不同配置。 |
相关的设计模式:

《图解设计模式》第六章中的案例其实就是使用了原型管理器。
原型管理器(Prototype Manager)也称为原型注册表。这个角色创建具体原型类的对象,并记录每一个被创建的对象。原型管理器的作用与工厂相似,其中定义了一个集合用于存储原型对象,如果需要某个对象的一个克隆,可以通过复制集合中对应的原型对象来获得。在原型管理器中针对抽象原型类进行编程,以便扩展。

抽象原型类 MyColor:
1 | public interface MyColor extends Cloneable { |
具体原型类 Red:
1 | public class Red implements MyColor { |
具体原型类 Blue:
1 | public class Blue implements MyColor { |
原型管理器类 PrototypeManager:
1 | public class PrototypeManager { |
客户端测试类 Client:
1 | public class Client { |
光看 UML 就能知道大概实现了。


对象和引用的区别:
一个对象可以有多个引用,但一个引用只能指向一个对象。当我们使用 == 比较对象时,一般比较的是对象地址。
引用拷贝示例:
1 | Teacher teacher = new Teacher("Taylor",26); |

引用拷贝通常发生在传递参数、返回值等场景中。例如,在 Java 中,如果将一个对象作为参数传递给方法,实际上是将该对象的引用传递给了方法,而不是对象本身的拷贝。
对象拷贝:创建对象本身的一个副本。
1 | Teacher teacher = new Teacher("Swift",26); |

深拷贝和浅拷贝都是对象拷贝。它们之间的主要区别在于是否复制了对象内部的数据。
🍐⚱️:(谈浅拷贝)你用魔法棒复制了自己,但是复制人和你都用着同样一台电脑)

在上图的例子中,单纯使用
clone()方法得到的是浅克隆的结果。
Object 类中的 clone() 方法clone 用于复制实例。clone 方法定义在 java.lang.Object 中,Object 又是所有类的父类,也就是说所有的 Java 类都继承了 clone 方法。但是,继承了该方法并不意味着就能直接用。能调用 clone 方法的前提是这个类必须实现了 Cloneable 接口。
Object 类中的 clone() 方法是通过 native 修饰的,也就是说它是通过直接调用底层操作系统方法实现的。当没有重写该方法时就默认调用这个方法。
clone() 和 new 一样,都可以创建一个新的对象。
new:当我们使用 new 关键字创建对象时,JVM 首先根据关键字后面的类型确定需要申请的内存大小,申请完内存后,执行类的构造方法。在执行构造方法期间,填充内存中各个属性域,这个填充的过程也叫初始化。构造方法执行完标志着对象创建成功,此时返回对象地址,在栈区以引用的方式调用对象。(当然,由于指令的重排序,发布对象可能在构造函数返回之前)clone():当我们调用某个对象的 clone() 方法克隆对象时,首先根据原对象的内存大小申请内存空间,申请完内存空间后,将原对象内存域复制到新申请的内存空间,复制完成标志着克隆完成,返回引用类型。也就是说,clone() 的过程没有调用类的构造函数。简单总结:new 和 clone() 第一步都是申请内存,只不过 new 关键字通过类构造方法初始化对象,clone() 方法直接通过克隆内存域完成对象创建。
clone() 是深克隆还是浅克隆?
int,boolean),那么它的深克隆和单纯 clone() 的浅克隆结果完全相同。clone() 的具体实现),那么它是深克隆,否则是浅克隆。手动赋值、第三方库也可以实现深拷贝。
Cloneable:重写 clone 方法在编码过程中,一般都是通过实现 java.lang.Cloneable 接口重写 clone() 方法,然后就可以调用重写后的 clone() 方法执行自己的逻辑。Cloneable 接口中并没有声明任何方法。他只是用来标记哪个类可以进行复制。这种接口称为「标记接口」。
Java 要求被克隆的类必须显式实现 Cloneable 接口。如果没有实现 Cloneable 接口的类的实例调用了 clone 方法, 则会在运行时抛出 CloneNotSupportedException 异常。
在实际应用开发中,浅克隆肯定不能满足所有业务场景。部分情况下,需要将浅克隆优化为深克隆,具体实现方法:实现 Cloneable 接口,重写 clone() 方法,在 clone() 方法中手动克隆引用属性。重写时记得 super.clone()。
示例代码:
1 | public class Test { |
实际上,上面的代码克隆得还是不够彻底:

想要做到完完整整的深克隆,必须保证所有引用属性克隆后都会创建新对象,并且这个过程需要无限向下递归,直到只剩下常量属性。想要实现这种程度的深克隆几乎是不可能的,因为一旦代码中引入 SDK 包中的类,且该类没有重写 clone() 方法,就无法实现深克隆。
Serializable:序列化后再反序列化把对象写到流里的过程是串行化(Serilization)过程,一种形象的说法为「冷冻」或者「腌咸菜(picking)」过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做「解冻」或者「回鲜(depicking)」过程。
将对象序列化,然后再反序列化成新的对象是深拷贝的一种方法。
示例代码:
1 | package top.uuanqin; |
通过 站内文章反射 生成对象,通过反射机制获取该对象的所有字段和属性信息。遍历所有字段和属性,以递归方式将源对象中的值复制到目标对象中。

通过 new 关键字生成实例是需要指定类名的。在开发过程中,我们需要「在不指定类名的前提下生成实例」的需求。
在以下情况,我们就不能根据类来生成实例,而要根据现有的实例来生成新的实例。

原型模式:
clone 创建出实例的副本。clone() 拷贝已有对象的数据,更新少量差值来实现。原型模式的基本工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象复制原型自己来实现创建过程。

登场角色:
Prototype(抽象原型类):定义用于复制现有实例来生成新实例的方法,可以是抽象类也可以是接口。在 Java 中,这个抽象类是实现了 @Cloneable 接口的。ConcretePrototype(具体原型类):实现复制现有实例并生成新实例的方法。除了将原始对象的数据复制到克隆体中之外,该方法有时还需处理克隆过程中的极端情况,例如克隆关联对象和梳理递归依赖等等。Client(使用者):使用复制实例的方法生成新实例。| 优点 | 缺点 |
|---|---|
| 解耦。克隆对象释放无需与它们所属的类相耦合。 | 克隆包含循环引用的复杂对象可能会非常麻烦。 |
| 克隆预生成原型,避免反复运行初始化代码。 | 需要为每一个类配备一个克隆方法,因此对已有类进行改造比较麻烦。需要修改其源代码,并且在实现深克隆时需要编写较为复杂的代码。 |
| 方便生成复杂对象。 | |
| 用继承以外的方式来处理复杂对象的不同配置。 |
相关的设计模式:

《图解设计模式》第六章中的案例其实就是使用了原型管理器。
原型管理器(Prototype Manager)也称为原型注册表。这个角色创建具体原型类的对象,并记录每一个被创建的对象。原型管理器的作用与工厂相似,其中定义了一个集合用于存储原型对象,如果需要某个对象的一个克隆,可以通过复制集合中对应的原型对象来获得。在原型管理器中针对抽象原型类进行编程,以便扩展。

抽象原型类 MyColor:
1 | public interface MyColor extends Cloneable { |
具体原型类 Red:
1 | public class Red implements MyColor { |
具体原型类 Blue:
1 | public class Blue implements MyColor { |
原型管理器类 PrototypeManager:
1 | public class PrototypeManager { |
客户端测试类 Client:
1 | public class Client { |
光看 UML 就能知道大概实现了。


对象和引用的区别:
一个对象可以有多个引用,但一个引用只能指向一个对象。当我们使用 == 比较对象时,一般比较的是对象地址。
引用拷贝示例:
1 | Teacher teacher = new Teacher("Taylor",26); |

引用拷贝通常发生在传递参数、返回值等场景中。例如,在 Java 中,如果将一个对象作为参数传递给方法,实际上是将该对象的引用传递给了方法,而不是对象本身的拷贝。
对象拷贝:创建对象本身的一个副本。
1 | Teacher teacher = new Teacher("Swift",26); |

深拷贝和浅拷贝都是对象拷贝。它们之间的主要区别在于是否复制了对象内部的数据。
🍐⚱️:(谈浅拷贝)你用魔法棒复制了自己,但是复制人和你都用着同样一台电脑)

在上图的例子中,单纯使用
clone()方法得到的是浅克隆的结果。
Object 类中的 clone() 方法clone 用于复制实例。clone 方法定义在 java.lang.Object 中,Object 又是所有类的父类,也就是说所有的 Java 类都继承了 clone 方法。但是,继承了该方法并不意味着就能直接用。能调用 clone 方法的前提是这个类必须实现了 Cloneable 接口。
Object 类中的 clone() 方法是通过 native 修饰的,也就是说它是通过直接调用底层操作系统方法实现的。当没有重写该方法时就默认调用这个方法。
clone() 和 new 一样,都可以创建一个新的对象。
new:当我们使用 new 关键字创建对象时,JVM 首先根据关键字后面的类型确定需要申请的内存大小,申请完内存后,执行类的构造方法。在执行构造方法期间,填充内存中各个属性域,这个填充的过程也叫初始化。构造方法执行完标志着对象创建成功,此时返回对象地址,在栈区以引用的方式调用对象。(当然,由于指令的重排序,发布对象可能在构造函数返回之前)clone():当我们调用某个对象的 clone() 方法克隆对象时,首先根据原对象的内存大小申请内存空间,申请完内存空间后,将原对象内存域复制到新申请的内存空间,复制完成标志着克隆完成,返回引用类型。也就是说,clone() 的过程没有调用类的构造函数。简单总结:new 和 clone() 第一步都是申请内存,只不过 new 关键字通过类构造方法初始化对象,clone() 方法直接通过克隆内存域完成对象创建。
clone() 是深克隆还是浅克隆?
int,boolean),那么它的深克隆和单纯 clone() 的浅克隆结果完全相同。clone() 的具体实现),那么它是深克隆,否则是浅克隆。手动赋值、第三方库也可以实现深拷贝。
Cloneable:重写 clone 方法在编码过程中,一般都是通过实现 java.lang.Cloneable 接口重写 clone() 方法,然后就可以调用重写后的 clone() 方法执行自己的逻辑。Cloneable 接口中并没有声明任何方法。他只是用来标记哪个类可以进行复制。这种接口称为「标记接口」。
Java 要求被克隆的类必须显式实现 Cloneable 接口。如果没有实现 Cloneable 接口的类的实例调用了 clone 方法, 则会在运行时抛出 CloneNotSupportedException 异常。
在实际应用开发中,浅克隆肯定不能满足所有业务场景。部分情况下,需要将浅克隆优化为深克隆,具体实现方法:实现 Cloneable 接口,重写 clone() 方法,在 clone() 方法中手动克隆引用属性。重写时记得 super.clone()。
示例代码:
1 | public class Test { |
实际上,上面的代码克隆得还是不够彻底:

想要做到完完整整的深克隆,必须保证所有引用属性克隆后都会创建新对象,并且这个过程需要无限向下递归,直到只剩下常量属性。想要实现这种程度的深克隆几乎是不可能的,因为一旦代码中引入 SDK 包中的类,且该类没有重写 clone() 方法,就无法实现深克隆。
Serializable:序列化后再反序列化把对象写到流里的过程是串行化(Serilization)过程,一种形象的说法为「冷冻」或者「腌咸菜(picking)」过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做「解冻」或者「回鲜(depicking)」过程。
将对象序列化,然后再反序列化成新的对象是深拷贝的一种方法。
示例代码:
1 | package top.uuanqin; |
通过 站内文章反射 生成对象,通过反射机制获取该对象的所有字段和属性信息。遍历所有字段和属性,以递归方式将源对象中的值复制到目标对象中。