专业的JAVA编程教程与资源

网站首页 > java教程 正文

探索Java设计模式--原型模式之浅拷贝与深拷贝

temp10 2024-10-19 14:54:39 java教程 15 ℃ 0 评论

导读:本文将主要讨论设计模式--原型模式中,关cloneable接口及浅拷贝与深拷贝的概念。

原型模式的理解

关于原型模式的理解,我在网上发现一个有趣且助于理解原型模式的例子在这里分享一下:火影忍者中鸣人的多重影分身。

探索Java设计模式--原型模式之浅拷贝与深拷贝

例:鸣人本体是原型对象,通过“多重影分身术”也就是原型模式(自己本身使用忍术进行创建),进行分身(复制)创建新对象(分身)。需要注意的是,创建新“分身”的人就是鸣人。这个意思就是说原型对象自己不仅是个对象还是个工厂。并且通过克隆方式创建的对象是全新的对象,它们都是有自己的新的地址,通常对克隆模式所产生的新对象(影分身)进行修改(攻击)是不会对原型对象(鸣人)造成任何影响的。每一个克隆对象都是相对独立的,通过不同的方式对克隆对象进行修改后,可以的到一系列相似但不完全相同的对象。

关于原型设计模式模型等这里就不做过详细的介绍,下面本文将主要讨论其中浅拷贝与深拷贝概念及cloneable接口。

Cloneable是一个标记接口

在设计模式--原型模式中,浅拷贝实现的方式为原型实现Cloneable接口并重写Object的clone方法实现。但是如果不实现cloneable接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。这是为什么?下面我们看看Cloneable类的源码。

通过查看Cloneable源码发现其内部没有定义其他任何信息,那么为什么在进行浅拷贝时(既调用Object的clone方法)必须要实现该接口呢?接下来,我们继续追踪查看一下Object的clone方法。

Object中的clone方法三个关注点

  • native关键字修饰
  • CloneNotSupportedException异常
  • 注释中大致描述:如果在没有实现Cloneable接口的实例上调用Object的clone()方法,则会导致抛出CloneNotSupporteddException,因此想实现clone的话,除了继承Object类外,还需要实现Cloneable接口

Object类中使用native对clone方法进行修饰,简单来说,被native关键字修饰的方法指的是一个java调用非java代码的接口。native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中,并编译成.dll文件。由于该方法不是由java语言实现,所以在源代码中没有实现体的。虽然Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。

所以判断是否实现cloneable接口的方式及clone方法的内部实现是在调用jvm中的实现体时进行实现的。由于clone方法是native方法,而native方法的效率远高于非native方法。同时clone并没有调用到构造函数,也就是说clone方法是不走构造方法。因此使用clone方法去做对象的拷贝效率上会高于new关键字构造的方式。

浅拷贝的实现

根据上面信息我们已经了解到了浅拷贝实现的原理,既通过实现CloneNotSupportedException接口及重写clone来实现浅拷贝,接下来通过一个Demo来帮助理解。(我们在参加招聘会时常常会先制作一份简历并进行”复制”多份以便于投递多个心仪的岗位)。

1、构建一个简历对象,并提供一个浅拷贝方法

2、进行简历制作及拷贝

浅拷贝与深拷贝的概念区分

接下来,还有一个重要点就是区分下浅拷贝与深拷贝的概念。

  • 浅拷贝:Object的clone提供的是浅拷贝,如果字段类型是值类型的,则对该字段进行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象。(既被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍指向原来的对象)
  • 深拷贝:复制对象所有的变量,包括把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象

浅拷贝复制不了引用类型字段所引用的对象

1、继续上面的例子,为了吸引招聘者的眼球,我们决定编写一块“工作经验”信息(Experience)并加入到简历中。首先是编写工作经验,包括公司名及年限。

2、将工作经验引用到简历中

3、完成简历,并复制多份

通过上面的例子发现,使用clone方式拷贝的对象当其属性字段为引用类型时,拷贝的并不是引用的对象而是引用。好比如说工作经验写在了简历一上,而在简历二上写上了一句“如果想了解我的工作经验请去查看简历一”,而不是真正的把工作经验写到简历二上。

深拷贝的实现

由于浅拷贝进行复制时对于引用类型字段只是复制了引用,而没有复制引用的对象。如果想要复制引用的对象,则需要采用深拷贝。Java中深拷贝实现的方式为:先通过序列化等方式将对象写到流中(等于复制了对象),但是原来的对象并没有动。再进行反序列化从流中读取对象并输出到另一个内存地址中去,通过以上流程便完成了深拷贝。下面通过一个Demo来帮助理解:

1、先将简历、工作经验类进行序列化,既实现Serializable接口

2、进行深度拷贝

通过上面的实验发现,Java中实现深度拷贝的原理是通过将对象写入流中再读取出对象并存到一个新内存地址中过去,完成了深拷贝。且复制了引用类型字段所引用的对象,并让引用指向了复制处理的新对象。所以通过采用深拷贝方式,当对简历1的工作经验进行修改时,由于简历1与简历2所引用的工作经验对象不是同一个,所以不会影响到简历2的内容信息。

总结

自此本文讨论关于设计模式--原型模式中cloneable接口及浅拷贝与深拷贝概念区分完结。下面做下简单总结:

  • 首先,cloneable是一个标记接口,要实现浅拷贝需要通过实现cloneable接口及重写Object的clone方法。Object的clone方法是一个由native关键字修饰,由java调用非java代码的接口。通过这种方式实现clone效率相对于使用new关键字构造方法要高。
  • 浅拷贝与深拷贝最主要的区别在于对象中引用类型的字段上复制的处理,浅拷贝复制的是引用而深拷贝复制的是引用的对象,并将引用指向新复制的对象。
  • 深拷贝实现的原理是通过序列化 + 流方式完成复制。

感谢您的阅读,如果喜欢本文欢迎关注和转发,本头条号将坚持原创,持续分享IT技术知识。对于文章内容有其他想法或意见建议等,欢迎提出共同讨论共同进步

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表