大家都知道java设计模式有一种原型模式prototype 那么我们平时所说的原型模式怎么实现呢,或者说我们在代码中应该怎么去写呢。咸鱼小哥这篇里用java的clone实现方式给大家分享一下我的理解。
我们都知道java lang 有一个规范接口叫做cloneable。它上面的注释提醒我们要使用这个接口,你应该去override object.clone方法。大部分工程师平时需要复制一个对象时通常时实现该接口。
假设现在我需要复制一个对象product.
通常的做法是这样的。重写java lang object clone 方法 okay 开始测试
通过测试我们发现两个对象的hashcode是不一样的,说明是两个不同的对象。。。
我们打开clone 方法看一下:
这是一个native 方法 ,通过本地c语言类库调用c的方法。
通过注释我们可以发现如果你实现了这个方法,假如你的对象有一个引用类型的话,那么你的所有被引用的对象也要重写这个方法,否则他实现的将是一个浅拷贝,而不是一个深拷贝。
深拷贝-完全创建了一个新的对象。和原来的对象没有任何关系。
浅拷贝-还是原来的对象,只不过我的引用变了。
okay ,那么当我们需要实现一个深拷贝的时候,假如你的对象中全部是java的八大基本类型或者封装类型(biginteger之类,),因为这些对象是不可变的,所以它实现的是一个深拷贝,但是如果你是一个复杂的自己定义的类型,那么你定义的这个类也要重写clone方法才能才能完整的实现深拷贝。比如:
我们增加了一个base对象,这个对象是一个引用对象,目前我没有对base对象重写clone方法。如果现在我们在拷贝product 对象完成之后,那么它将是一个浅拷贝,就是说base 对象还是原来的对象,只不过我多了一个引用。指向的还是旧的对象。
okay 我来测试一下
通过测试结果来看 确实base 对象还是原来的,如我们猜想,这就不是完全的深拷贝,一旦我改变克隆对象的值,那么他的原型对象也会被改变。显然这不是我们想要的样子。
okay go on,,,,,
当我clone完成之后改了这行代码测试后发现
两个对象的值都变了。。那么如何才能保证,只有一个变呢。。。。很简单我们只要多实现一个clone方法就可以了
相应的,你的product 也要重写:
测试代码
测试结果:
我们发现是不同的对象了,也解耦了对象。。。。
那么我们除了实现jdk的clone 方法来做copy外,还有什么其他方式呢。。。。。答案many roads to roman。。。。
我们可以通过序列化的方式来做clone 测试一下:
效果相同,,,but 呢,这个方式是cpu密集型方式,不适合我们来写。。。不推荐。。。。
okay jdk中也有其他的实现 比如:
arraylist中的clone 但是这个是showcopy 浅拷贝 ,
spring的AbstractBeanDefinition也有clone方法,但不是基于jdk实现的,更加灵活,,
所以大家平时用的时候看具体场景使用即可。。。。
总结一下 :我们用原型模式可以做到一下两点优化:
1.避免重复的初始化代码。
2.更方便的构建复杂对象。
okay 小咸鱼只是以clone实现原型模式的例子来讲解,其实原型模式还有其他实现 ,但思想是一样的 ,大家可以自己随意挥发。。。。。