Java中的clone()----深复制,浅复制

这篇文章主要介绍了Java中对象的深复制(深克隆)和浅复制(浅克隆) ,需要的朋友可以参考下

1.浅复制与深复制概念

⑴浅复制(浅克隆)

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

⑵深复制(深克隆)

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

2.Java的clone()方法

⑴clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足: 
①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象 
②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样 
③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

⑵Java中对象的克隆

①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。 
②在派生类中覆盖基类的clone()方法,并声明为public。 
③在派生类的clone()方法中,调用super.clone()。 
④在派生类中实现Cloneable接口。

请看如下代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public class Student implements Cloneable

{

  String name;

 int age;

  Student(String name,int age)

  {

  this.name=name;

  this.age=age;

  }

 public Object clone()

  {

   Object o=null;

  try

   {

   o=(Student)super.clone();//Object 中的clone()识别出你要复制的是哪一个对象。

   }

  catch(CloneNotSupportedException e)

   {

    System.out.println(e.toString());

   }

  return o;

  

 

 public static void main(String[] args)

  {

  Student s1=new Student("zhangsan",18);

  Student s2=(Student)s1.clone();

  s2.name="lisi";

  s2.age=20;

  //修改学生2后,不影响学生1的值。

  System.out.println("name="+s1.name+","+"age="+s1.age);

  System.out.println("name="+s2.name+","+"age="+s2.age);

 }

}

说明: 
①为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。 
②继承自java.lang.Object类的clone()方法是浅复制。以下代码可以证明之。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

class Professor

{

  String name;

  int age;

  Professor(String name,int age)

  {

  this.name=name;

  this.age=age;

  }

}

public class Student implements Cloneable

{

  String name;// 常量对象。

  int age;

  Professor p;// 学生1和学生2的引用值都是一样的。

  Student(String name,int age,Professor p)

  {

  this.name=name;

  this.age=age;

  this.p=p;

  }

 public Object clone()

  {

   Student o=null;

  try

   {

    o=(Student)super.clone();

   }

  catch(CloneNotSupportedException e)

   {

    System.out.println(e.toString());

   }

   o.p=(Professor)p.clone();

  return o;

  

 public static void main(String[] args)

 {

  Professor p=new Professor("wangwu",50);

  Student s1=new Student("zhangsan",18,p);

  Student s2=(Student)s1.clone();

  s2.p.name="lisi";

  s2.p.age=30

  System.out.println("name="+s1.p.name+","+"age="+s1.p.age);

  System.out.println("name="+s2.p.name+","+"age="+s2.p.age);

  //输出结果学生1和2的教授成为lisi,age为30。

  }

}

那应该如何实现深层次的克隆,即修改s2的教授不会影响s1的教授?代码改进如下。 
改进使学生1的Professor不改变(深层次的克隆)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

class Professor implements Cloneable

{

  String name;

  int age;

  Professor(String name,int age)

  {

  this.name=name;

  this.age=age;

  }

 public Object clone()

  {

   Object o=null;

  try

   {

    o=super.clone();

   }

  catch(CloneNotSupportedException e)

   {

    System.out.println(e.toString());

   }

  return o;

  }

}

public class Student implements Cloneable

{

  String name;

  int age;

  Professor p;

  Student(String name,int age,Professor p)

  {

  this.name=name;

  this.age=age;

  this.p=p;

  }

 public Object clone()

  {

   Student o=null;

  try

   {

    o=(Student)super.clone();

   }

  catch(CloneNotSupportedException e)

   {

    System.out.println(e.toString());

   }

   //对引用的对象也进行复制

   o.p=(Professor)p.clone();

  return o;

  

 public static void main(String[] args)

  {

  Professor p=new Professor("wangwu",50);

  Student s1=new Student("zhangsan",18,p);

  Student s2=(Student)s1.clone();

  s2.p.name="lisi";

  s2.p.age=30;

  //学生1的教授不 改变。

  System.out.println("name="+s1.p.name+","+"age="+s1.p.age);

  System.out.println("name="+s2.p.name+","+"age="+s2.p.age);

 }

}

3.利用串行化来做深复制(主要是为了避免重写比较复杂对象的深复制的clone()方法,也可以程序实现断点续传等等功能) 
    把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。 
    应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。 
    在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。 
如下为深复制源代码。


1

2

3

4

5

6

7

8

9

10

11

public Object deepClone()

{

 //将对象写到流里

 ByteArrayOutoutStream bo=new ByteArrayOutputStream();

 ObjectOutputStream oo=new ObjectOutputStream(bo);

 oo.writeObject(this);

 //从流里读出来

 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());

 ObjectInputStream oi=new ObjectInputStream(bi);

 return(oi.readObject());

}

这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象或属性可否设成transient,从而将之排除在复制过程之外。上例代码改进如下。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

class Teacher implements Serializable{

  String name;

  int age;

  public void Teacher(String name,int age){

  this.name=name;

  this.age=age;

  }

}

public class Student implements Serializable{

 String name;//常量对象

 int age;

 Teacher t;//学生1和学生2的引用值都是一样的。

 public void Student(String name,int age,Teacher t){

  this.name=name;

  this.age=age;

  this.p=p;

 }

 public Object deepClone() throws IOException,

    OptionalDataException,ClassNotFoundException{//将对象写到流里

  ByteArrayOutoutStream bo=new ByteArrayOutputStream();

  ObjectOutputStream oo=new ObjectOutputStream(bo);

  oo.writeObject(this);//从流里读出来

  ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());

  ObjectInputStream oi=new ObjectInputStream(bi);

  return(oi.readObject());

 }

 public static void main(String[] args){

  Teacher t=new Teacher("tangliang",30);

  Student s1=new Student("zhangsan",18,t);

  Student s2=(Student)s1.deepClone();

  s2.t.name="tony";

  s2.t.age=40;

  //学生1的老师不改变

  System.out.println("name="+s1.t.name+","+"age="+s1.t.age);

 }

}

时间: 2024-10-13 10:50:32

Java中的clone()----深复制,浅复制的相关文章

OpenCv学习笔记(四)--Mat基本图像容器Mat对象信息头,矩阵体的创建,深复制,浅复制详解

1--我们知道Mat是一个图像容器类,这个数据结构由两部分组成: 1--矩阵头--即class Mat类所实例化的类对象所开辟的空间里面存储的数据---就是这个矩阵的信息,当我们以 Mat object;这样声明类对象的时候,也仅仅是创建了一个Mat的信息头,并没有创建矩阵体,也就是说,我们并 没有给将要存储的图像开辟相应的空间 2--矩阵头--包含: 1--矩阵的尺寸----比如---class Mat这个类中的----数据成员rows,cols---就可以指定图像的尺寸 2--存储方法---

简单理解php深复制浅复制问题

其实接触深复制浅复制是通过学习c++了解到的,比如c++很好用的模板,php是不允许方法模板和类模板 一个简单的例子,如果不是很了解php 的取地址符&,可以去看下官方文档,php的&就是别名 class zx{ public $r ='44444444<br>'; public function __construct() { } public function ccccc() { echo $this->r; } } $rr = new zx(); //$ee = $

深复制 浅复制

深复制:不仅复制对象 还复制对象关联的对象 浅复制:只是复制对象本身 不复制对象关联的对象 // // main.m // 对象的复制 // // Created by MAC on 15/12/20. // Copyright © 2015年 MAC. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * ar

JAVA中的clone方法剖析

原文出自:http://blog.csdn.net/shootyou/article/details/3945221 java中也有这么一个概念,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的?     1. Clone&Copy     假设现在有一个Employee对象,Employee tobby =new Employee(“CMTobby”,5000),通常我们会有这样的赋值Employee cindyelf=tobby,这个时候只

Java中对Clone的理解

面试中经常遇到Clone的相关知识,今天总算是把Clone理解的比较透彻了!Java中Clone的概念大家应该都很熟悉了,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的?     1. Clone和Copy     假设现在有一个User对象,User u1=new User(“U1001”,“Jason”,25),通常我们会有这样的赋值User u2=u1,这个时候只是简单了copy了一下reference,u2和u1都指向内存中同一个ob

java中的clone:深复制和浅复制

参考文章: java中的深复制和浅复制 : http://blog.csdn.net/naughty610/article/details/6598943 Java 深拷贝和浅拷贝: http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html 

Java中的深拷贝(深复制)和浅拷贝(浅复制)

深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在C++语言中,若不弄懂,则会在delete的时候出问题,但是我们在这幸好用的是Java.虽然java自动管理对象的回收,但对于深拷贝(深复制)和浅拷贝(浅复制),我们还是要给予足够的重视,因为有时这两个概念往往会给我们带来不小的困惑. 浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象.深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象.举例来说更加清楚:对象A1中包含对B1的引用

【C/C++】复制构造函数、深复制浅复制

常见问题 Q1. 下面代码的输出结果是( )? 1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 public: 7 MyClass(int n) { number = n; } 8 MyClass(const MyClass &other) 9 { 10 number = other.number; 11 cout << "a "; 12 } 13 private:

iOS学习(二)之深复制&amp;浅复制

1.无论是深复制还是浅复制,被复制的对象类型是不变的.此对象类型具有什么功能就具有什么功能,不会因为自行修改了返回对象的指针类型而改变. 比如: 这里的str和str1的值和指针地址完全一样,因为是不可变类型使用了copy,什么都不变.为什么NSNumber类型的指针可以指向NSSring类型copy出的对象呢?因为copy返回类型为id万能指针类型.如果返回的是instancetype就会警告. 记住:对于不可变的临时变量使用浅复制copy没什么用,地址和值都一样. 2.怎么理解最后一句话?