java克隆(clone)的两种实现方法 您所在的位置:网站首页 java复制对象方法 java克隆(clone)的两种实现方法

java克隆(clone)的两种实现方法

2023-09-15 06:34| 来源: 网络整理| 查看: 265

1概念

由java API文档可知:

clone 属于 java.long.Object的一个方法

protected Object clone() throws CloneNotSupportedException

创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式:x.clone() != x为 true,表达式:x.clone().getClass() == x.getClass()

也为 true,但这些并非必须要满足的要求。一般情况下:x.clone().equals(x)为 true,但这并非必须要满足的要求。按照惯例,返回的对象应该通过调用 super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。

按照惯例,此方法返回的对象应该独立于该对象(正被复制的对象)。要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被复制对象的内部“深层结构”的所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。Object 类的 clone 方法执行特定的复制操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。注意,所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我复制。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。

 Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。

 

2 浅克隆和深克隆 2.1 概念

如上所述,java在克隆的时候会复制并返回对象的一个副本(其中包含所有字段和基础数据类型字段的数值),如果对象中存在复合数据类型(比如数组变量、其他对象等),则只复制复合数据类型的引用地址,不会复制复合数据中的具体值,而复制对象中的基础数据类型(比如int,long,float,String等)则会把数值也会复制过去。因此这种克隆方式称为“浅克隆”。同理可得,“深克隆”就是将基础数据类型和所有复合数据类型都一并复制拷贝一份,相互之间互不影响。

浅克隆是存在弊端的,假如克隆的两个对象其中一个更改了自己对象中的复合数据中的数据,则另一个对象中的数据也会同步被修改,因为他们引用的是同一个复合数据对象。

2.2 实现方式

实现克隆的方式主要有两种:

(1)、对象实现Cloneable接口并重写Object类中的clone()方法(浅克隆方式);

 代码如下:

(a)用户信息 public class Person implements Cloneable{ private String name; private String sex; private Phone phone; /** * 实现Cloneable接口,并重写clone方法 */ @Override protected Object clone(){ Person p=null; try { /** * 若要实现深克隆,此处就必须将对象中所有的复合数据类型统统单独复制拷贝一份, * 但是实际开发中,无法确定对象中复合数据的种类和个数, * 因此一般不采用此种方式实现深克隆 */ p = (Person) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return p; } public Person(String name, String sex, Phone phone) { super(); this.name = name; this.sex = sex; this.phone = phone; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Phone getPhone() { return phone; } public void setPhone(Phone phone) { this.phone = phone; } @Override public String toString() { return "[name=" + name + ", sex=" + sex + ", phone=" + phone + "]"; } } (b)手机信息 public class Phone{ private String number; private String area; public Phone(String number, String area) { super(); this.number = number; this.area = area; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getArea() { return area; } public void setArea(String area) { this.area = area; } @Override public String toString() { return "[number=" + number + ", area=" + area + "]"; } } (c)测试代码 public static void main(String[] args) { Person p=new Person("雷小涛", "man", new Phone("123456", "四川成都")); Person p2=(Person) p.clone(); System.out.println("p:"+p.toString()); System.out.println("p2:"+p2.toString()); p2.setName("leixiaotao"); p2.getPhone().setArea("四川乐山"); System.out.println("-------其中一个对象修改值过后-------"); System.out.println("p:"+p.toString()); System.out.println("p2:"+p2.toString()); }

(d)结果:修改基础数据结构数值不会影响其他对象,修改复合数据数值,全部都会受影响

 

 (2)、对象实现Serializable接口,通过对象的序列化和反序列化实现克隆(此方法可以实现真正的深度克隆)。

代码如下:

(a)用户信息类 public class Person implements Serializable{ private static final long serialVersionUID = -3936148364278781089L; private String name; private String sex; private Phone phone; public Person(String name, String sex, Phone phone) { super(); this.name = name; this.sex = sex; this.phone = phone; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Phone getPhone() { return phone; } public void setPhone(Phone phone) { this.phone = phone; } @Override public String toString() { return "[name=" + name + ", sex=" + sex + ", phone=" + phone + "]"; } } (b)手机信息类 public class Phone implements Serializable{ private static final long serialVersionUID = -4482468804013490322L; private String number; private String area; public Phone(String number, String area) { super(); this.number = number; this.area = area; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getArea() { return area; } public void setArea(String area) { this.area = area; } @Override public String toString() { return "[number=" + number + ", area=" + area + "]"; } } (c)CloneUtil工具类 public class CloneUtil { @SuppressWarnings("unchecked") public static T clone(T object) throws Exception{ ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout); oos.writeObject(object); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); // 此处不需要释放资源,说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义 // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放 return (T) ois.readObject(); } } (d)测试代码 public static void main(String[] args) throws Exception { Person p=new Person("雷小涛", "man", new Phone("123456", "四川成都")); // Person p2=(Person) p.clone(); Person p2=CloneUtil.clone(p); System.out.println("p:"+p.toString()); System.out.println("p2:"+p2.toString()); p2.setName("leixiaotao"); p2.getPhone().setArea("四川乐山"); System.out.println("-------其中一个对象修改值过后-------"); System.out.println("p:"+p.toString()); System.out.println("p2:"+p2.toString()); }

(e)结果

 

 【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有