一、为什么要克隆
Java中的克隆是指创建一个与原始对象完全相同的对象,包括他的属性和值。在Java中,我们可以通过实现Cloneable接口并覆盖Object类中的clone()方法来是想克隆。
为什么不能通过new一个对象来实现克隆?
new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone()方法了
误区:
我们常见的
Student stu1 = new Student();
Student stu2 = stu1;
这种形式的代码复制是引用,即对象在内存中的地址,stu1和stu2还是指向了同一个对象,这种只能成为引用复制,两个引用指向的还是同一个对象。
二、如何实现对象克隆
在Java中,实现克隆步骤如下:
1.要让被克隆的类实现Cloneable接口。该接口是一个标记接口,表示该类要进行克隆操作
public class Person implements Cloneable {
}
2.使用clone()方法来克隆对象
public class Person implements Cloneable {
int num;
String name;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//复制对象
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
3.测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(100,"jim");
Person p2 =p1.clone();//clone方法 返回一个新对象
System.out.println(p1);
System.out.println(p2);
System.out.println(p1==p2);//false
}
}
4.运行结果:
由上述结果可知:对象克隆是将一个已经存在的对象以及对象的数据克隆到另一个对象上。即克隆是两个不同对象。
三、浅克隆和深克隆。
浅克隆和深克隆
浅克隆:克隆一个对象时,如果对象中有关联对象,只会将关联对象的地址复制,不会克隆关联对象。
深克隆:克隆一个对象时,如果对象中有关联对象,并且将关联对象也一同克隆(创建一个新的对象),会克隆关联对象。
如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,即原型对象和克隆对象的成员变量指向相同的内存地址。
实现方式:
1.在Java语言中,通过覆盖Object类中的clone()方法可以实现克隆
2.在spring框架中提供BeanUtils.copyProperties(source,target);序列化,对象输入和输出流
浅克隆:
描述:我要克隆Person对象,而且Person对象中还关联者Address是对象,那么克隆的结果是什么?
public class Person implements Cloneable{
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
public class Address {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
/*
* 浅克隆:克隆一个对象时,如果对象中有关联的对象,只将关联对象的地址复制过来
* */
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("汉中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.clone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1);//jim 西安
System.out.println(p2);//tom 西安
}
}
运行结果:
那么我们如何将浅克隆转为深克隆呢?
方式一:
在每一层类中都重写clone()方法,类实现Cloneable接口。这种不适合层级多的关联对象,太麻烦
首先,在要让Address的类实现Cloneable接口。
在Address类重写clone方法
// 实现深克隆的方式
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
在Person类中改:
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
person.address = (Address)address.clone(); //深度复制 联同person中关联的对象也一同克隆.
return person;
}
这样就实现了深度克隆
运行结果:
方式二(解决多层克隆问题):
使用序列化方式,对象的输入和输出流
package com.ffyc.javapro.objectClone.demo3;
import java.io.*;
public class Person implements Serializable {
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 自定义克隆方法
* @return
*/
public Person myclone() {
Person person = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
person = (Person) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
测试类中
Person p2 =p1.myclone();
可以实现深克隆。方式二比方式一更优化,即使address还关联其他对象,也能克隆出来