1、什么是深克隆与浅克隆?
- 浅克隆:在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。 - 深克隆:在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
2、怎样实现对象的浅克隆?
将“某电商平台类”进行浅克隆。
/*
* 买家类(某电商平台)
*/
public class Buyer {
private String buyerNick; //买家昵称
private char sex; //买家性别
private int integral; //买家积分
// 构造方法
public Buyer(String buyerNick, char sex, int integral) {
this.buyerNick = buyerNick;
this.sex = sex;
this.integral = integral;
}
@Override
public String toString() {
return "买家信息 [买家昵称=" + buyerNick + ", 买家性别=" + sex + ", 买家积分=" + integral + "]";
}
public String getBuyerNick() {
return buyerNick;
}
public void setBuyerNick(String buyerNick) {
this.buyerNick = buyerNick;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getIntegral() {
return integral;
}
public void setIntegral(int integral) {
this.integral = integral;
}
}
- 实现Cloneable接口:
public interface Cloneable {
}
通过Cloneable接口的源码可知:该接口为一个空接口,所以该接口是一个标示,标示该类可以被克隆,如果没有实现Cloneable接口当调用克隆方法时候会抛出CloneNotSupportedException异常!
2. 覆盖Object中的clone方法:
protected native Object clone() throws CloneNotSupportedException;
通过Object类中的clone方法的源码可知:该方法是受保护的所以只能由子类重写后调用。
重写后:
//对象的浅克隆
@Override
public Buyer clone() throws CloneNotSupportedException {
return (Buyer)super.clone();
}
- 对浅克隆进行测试
/**
* 对象的浅克隆
*
*/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建买家对象1
Buyer buyer01 = new Buyer("张无忌", '男', 12800);
// 创建买家对象2(与买家1数据一样)
// 复制对象buyer01产生新的对象。
Buyer buyer02 = buyer01.clone();
// 修改买家2的买家昵称。
// 修改买家2昵称不会造成买家1昵称的改变。
buyer02.setBuyerNick("东方不败");
System.out.println(buyer01);
System.out.println(buyer02);
}
}
运行结果:
买家信息 [买家昵称=张无忌, 买家性别=男, 买家积分=12800]
买家信息 [买家昵称=东方不败, 买家性别=男, 买家积分=12800]
提出问题:当Buyer类中添加一个引用属性Adress(送货地址类)
/*
* 送货地址类
*/
public class Address{
private String contact; //联系人
private String addressContent; //具体地址
private String phoneNumber; //联系手机号码
public Address(String contact, String addressContent, String phoneNumber) {
super();
this.contact = contact;
this.addressContent = addressContent;
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return "地址信息 [contact=" + contact + ", addressContent=" + addressContent + ", phoneNumber=" + phoneNumber
+ "]";
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public String getAddressContent() {
return addressContent;
}
public void setAddressContent(String addressContent) {
this.addressContent = addressContent;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
package com.apesource.demo01;
/*
* 买家类(某电商平台)
*/
public class Buyer{
private String buyerNick; //买家昵称
private char sex; //买家性别
private int integral; //买家积分
private Address address;//送货地址
// 构造方法
public Buyer(String buyerNick, char sex, int integral) {
this.buyerNick = buyerNick;
this.sex = sex;
this.integral = integral;
}
@Override
public String toString() {
return "买家信息 [买家昵称=" + buyerNick + ", 买家性别=" + sex + ", 买家积分=" + integral + ",送货地址=" + address + "]";
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getBuyerNick() {
return buyerNick;
}
public void setBuyerNick(String buyerNick) {
this.buyerNick = buyerNick;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getIntegral() {
return integral;
}
public void setIntegral(int integral) {
this.integral = integral;
}
}
如果按照浅克隆的方式对Buyer类的对象进行克隆,那么不会实现对属性address的克隆:所以要进行深克隆。
3、怎样实现对象的深克隆?
通过覆盖Object类中的clone方法进行深克隆!
- 实现Cloneable接口:
- 覆盖Object中的clone方法:(当然)
Address类中实现Cloneable接口覆盖clone方法。
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
Buyer类中实现Cloneable接口覆盖clone方法。
//深克隆
@Override
protected Buyer clone() throws CloneNotSupportedException {
//克隆新的买家对象
Buyer cloneBuyer = (Buyer)super.clone();
//克隆买家对象中的收货地址对象。
Address cloneAddress = cloneBuyer.getAddress().clone();
cloneBuyer.setAddress(cloneAddress);
return cloneBuyer;
}
注意:Buyer中的clone方法需要手动调用Address的clone方法对属性进行克隆。
测试:
/*
* 深复制
*/
public class Test03 {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建买家对象1
Buyer buyer01 = new Buyer("西门吹牛",'男', 250);
// 为买家对象1设置默认的送货地址
Address address = new Address("西门吹牛","西安市钟楼","13966554431");
buyer01.setDefaultAddres(address);
// 复制买家对象1(克隆)
Buyer buyer02 = buyer01.clone();
// 改变买家2的收货地址中的联系人
buyer02.getDefaultAddres().setContact("孙悟空");
System.out.println("买家1:" + buyer01);
System.out.println("买家2:" + buyer02);
System.out.println("----------------------------------");
System.out.println("买家对象1:" + buyer01.hashCode());
System.out.println("买家对象2:" + buyer02.hashCode());
System.out.println("----------------------------------");
System.out.println("买家对象1的默认收货地址对象:" + buyer01.getDefaultAddres().hashCode());
System.out.println("买家对象2的默认收货地址对象:" + buyer02.getDefaultAddres().hashCode());
}
}
运行结果:
买家1:买家信息 [买家昵称=西门吹牛, 买家性别=男, 买家积分=250,送货地址=地址信息 [contact=西门吹牛, addressContent=西安市钟楼, phoneNumber=13966554431]]
买家2:买家信息 [买家昵称=西门吹牛, 买家性别=男, 买家积分=250,送货地址=地址信息 [contact=孙悟空, addressContent=西安市钟楼, phoneNumber=13966554431]]
----------------------------------
买家对象1:366712642
买家对象2:1829164700
----------------------------------
买家对象1的默认收货地址对象:2018699554
买家对象2的默认收货地址对象:1311053135
通过对象的序列化的方法进行深克隆!
源码:
import java.io.Serializable;
/*
* 武器类
* 使用序列化和反序列化,实现"深拷贝"
*/
public class Weapon implements Serializable {
/**
*
*/
private static final long serialVersionUID = -198218457098370816L;
private String name; //武器名称
private int number; //武器子弹数量
public Weapon(String name, int number) {
super();
this.name = name;
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import com.apesource.demo02.Weapon;
/*
* 机器人类
* 使用序列化和反序列化,实现"深拷贝"
*/
public class Robot implements Serializable{
/**
*
*/
private static final long serialVersionUID = 408666801749150102L;
private String level; //机器人等级
private int value; //机器人的生命值
private Weapon weapon; //机器人的武器
public Robot(String level, int value) {
super();
this.level = level;
this.value = value;
}
// 自定义的"深拷贝"方法
public Robot deepClone() throws Exception{
// 将“当前Robot对象”转换为字节流(序列化)
ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节数组输出流
ObjectOutputStream oos = new ObjectOutputStream(bos); //对象输出流(用于实现序列化)
oos.writeObject(this); //将当前Robot对象对象,写入字节数组输出流bos
// 将字节流转换为“新的Robot对象”(反序列化)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); // 创建字节数组输入流,传入"序列化后的字节流"
ObjectInputStream ois = new ObjectInputStream(bis); //对象输入流(用于实现反序列化)
Robot newRobot = (Robot)ois.readObject(); //将字节流转换为一个新的Robot对象
return newRobot;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
}