对象克隆
1. 为什么要克隆?
大家先思考一个问题,为什么需要克隆对象?直接 new 一个对象不行吗?
克隆的对象可能包含一些已经修改过的属性,而 new 出来的对象的属性都还 是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠 clone 方法了。那么我把这个对象的临时属性一个一个的赋值给我新 new 的对 象不也行嘛?可以是可以,但是一来麻烦不说,二来,大家通过上面的源码都发 现了 clone 是一个 native 方法,就是快啊,在底层实现的。
在实际的项目开发中从前端返回后端的数据我们一般都用一个Model来实现封装,而实际模型中同一个模型类又可分好几种,如专门负责接收前端数据的,可以加入验证. 向dao层传输数据的这里就需要用到对象的克隆.
误区:
我们常见的 Student stu1 = new Student ();
Student stu2 = stu1 ;
这种形式的代码复制的是引用,即对象在内存中的地址,
a 和 b 对象仍然指向了同一个对象。这种只能称为引用复制,两个引用指向的还是同一个对象.
2 .如何实现克隆
先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。 在 Java 语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包 括 int、double、byte、boolean、char 等简单数据类型,引用类型包括类、 接口、数组等复杂类型。基本类型的值可以直接复制,引用类型只能复制引用地 址。所以浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。
3. 浅克隆和深克隆
1、浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也 就是说原型对象和克隆对象的成员变量指向相同的内存地址。 简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制
一个浅克隆代码:
类及克隆代码
package objectClone.demo1;
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 + '\'' +
'}';
}
}
测试
package objectClone.demo1;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(100,"jim");
Person p2 =p1.clone();
System.out.println(p1==p2);
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3i0WUsC-1678864162939)(C:\Users\封纪元\AppData\Roaming\Typora\typora-user-images\1642637131998.png)]
2、深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。 简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
在 Java 语言中,如果需要实现深克隆,可以通过覆盖 Object 类的 clone()方法 实现,也可以通过序列化(Serialization)等方式来实现。 序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可 以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其 读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现 Serializable 接口,否则无法实现序列化操作。
深克隆clone代码实例:
引用类:
package objectClone.demo2;
public class Address implements Cloneable{
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
}
实体类:
package objectClone.demo2;
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();
person.address = (Address)address.clone(); //深度复制 联同person中关联的对象也一同克隆.
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
测试:
package objectClone.demo2;
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);
System.out.println(p2);
}
}
结果:
序列化方式代码实例:
引用类:
package objectClone.demo4;
import java.io.Serializable;
public class Address implements Serializable {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
实体类
package objectClone.demo4;
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 +
'}';
}
}
测试:
package objectClone.demo4;
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.myclone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1);
System.out.println(p2);
}
}
结果:
4 .解决多层克隆问题
如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用 类型,使用 clone 方法就会很麻烦。这时我们可以用序列化的方式来实现对象的 深克隆。