Java的对象克隆
克隆
直接使用new关键字创建的对象,是一个新的对象,没有任何数据(初始化的默认值)
使用克隆创建的对象,可以复制对象的数据
Java中数据类型有值类型(八大基本数据类型)和引用类型(类,数组,接口)
基本类型复制值,引用类型复制引用地址而不是对象本身
浅克隆、深克隆区别在于是否支持引用类型的成员变量的复制
1.1浅克隆 ShallowClone
- 如果对象的成员变量是基本类型,克隆对象会拿到成员的值
- 如果对象的成员变量是引用类型,克隆对象会拿到成员的引用地址
在浅克隆中:源对象和克隆对象的成员变量指向相同的内存地址
浅克隆实现:
1. 重写Object类的clone()方法
示例1
直接重写Object的clone(),会抛出异常
正确用法:1.先实现实现Cloneable接口,再重写clone()方法
测试
示例2
作为属性的类(没有重写clone方法)
public class Address{
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
被克隆的类(重写了clone方法)
public class Person implements Cloneable {
private String name;
private Address address;
public Person() {}
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
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{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
}
测试
两个对象关联的同一个Address对象,Address修改了,克隆对象的信息也发生了变化
2. 在spring中提供了BeanUtils.copyProperties(source,target);
1.2深克隆 DeepClone
在深克隆中,无论源对象的成员是基本类型/引用类型,都会复制给克隆对象
也就是在深克隆中,对象和所有成员都会被克隆
而在浅克隆中,只会克隆对象和基本类型成员的信息,和引用类型的地址
实现深克隆的方式
1、实现Cloneable接口,重写Object的clone()方法
- 原对象重写clone方法,在clone中,复制源对象和源对象的属性
- 原对象关联的对象重写clone方法
当做属性的类(重写clone方法)
public class Address implements Cloneable {
private 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();
}
}
被克隆的类(重写clone方法)
package com.example.java_hign.clone.demo2;
public class Person implements Cloneable {
private String name;
private Address address;
public Person() {}
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
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中关联的对象也一同克隆.
person.address = (Address) address.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
}
测试
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("汉中");
Person p1 = new Person("刘备",address);
Person p2 =p1.clone();
p2.setName("李世明");
address.setAddress("西安");
System.out.println(p1);// Person{name='刘备', address=Address{address='西安'}}
//深度克隆:复制了源对象和源对象的关联对象,属性不再指向同一对象
System.out.println(p2);//Person{name='李世明', address=Address{address='汉中'}}
}
2、通过序列化实现(Serialization),不用在关联的对象中也重写Clone()
被关联的类Address(实现Serializable 序列化接口)
public class Address implements Serializable {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
被克隆的源对象(实现Serializable 序列化接口)
public class Person implements Serializable {
private String name;
private Address address;
public Person() {}
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
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;
}
/**
* 自定义克隆方法
*/
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{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
}
测试
总结:
- 浅克隆中,克隆对象的引用对象和原对象使用的同一个对象
- 深克隆中,克隆对象的引用对象都是重新创建的对象