💁 个人主页:黄小黄的博客主页
❤️ 支持我:👍 点赞 🌷 收藏 🤘关注
🎏 格言:All miracles start from sometime somewhere, make it right now.
本文来自专栏:JavaSE从入门到精通
文章目录
- 1 在Java中如何实现对象的拷贝呢?
- 2 浅拷贝与深拷贝引出
- 3 如何实现深拷贝
- 4 深拷贝实现完整代码
- 写在最后
1 在Java中如何实现对象的拷贝呢?
Clonable 接口
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”。 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException
异常。
我们来简单看一个拷贝的例子:
在该例子中,实现了对 person 对象的拷贝, Person 类中只含有一个属性:name
/**
* @author 兴趣使然黄小黄
* @version 1.0
* 克隆案例
*/
public class CloneDemo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("黄小黄");
Person p2 = p1.clone();
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1 = p2 ? " + (p1 == p2));
}
}
class Person implements Cloneable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 实现克隆Person对象
@Override
protected Person clone() {
Person p = null;
try {
p = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
重点关注一下,clone方法的实现。测试结果如下:
这样一个简单的克隆demo就完成了!
此时我们 尝试修改p2对象中的name,执行如下代码后,得出结论p2的修改不会影响p1的内容。
Person p1 = new Person();
p1.setName("黄小黄");
Person p2 = p1.clone();
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1 = p2 ? " + (p1 == p2));
System.out.println("修改p2.name为懒羊羊");
p2.setName("懒羊羊");
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
然而真的是这样吗?如果Person类中含有引用数据类型的话,还能正常拷贝吗?我们继续往后看。
2 浅拷贝与深拷贝引出
观察下面的代码,我们在上述克隆例子中的Person类,新增一个属性,该属性属于Wallet类,Wallet类中包含一个属性money。我们再来拷贝一下试试:
执行如下代码进行测试,尝试在克隆完成后,修改p2的wallet类型的money值:
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("黄小黄");
Wallet wallet = new Wallet();
wallet.setMoney(100);
p1.setMyMoney(wallet);
Person p2 = p1.clone();
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
System.out.println("p1 = p2 ? " + (p1 == p2));
System.out.println("===修改p2的wallet属性为999===");
wallet.setMoney(999);
p2.setMyMoney(wallet);
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
}
其结果如下:p2的修改竟然影响到了p1对象的wallet值!
⭐️ 从内存角度我们来看一看在修改wallet的时候发生了什么:
- 在使用clone进行克隆后,p1和p2为两个不同的对象,指向堆区的不同空间,其中,克隆出来的wallet属性,仅仅是拷贝了wallet的值(哈希地址),因此,虽然p1、p2为两个不同的对象,但是却指向了相同的空间。
- 此时如果修改wallet的money100修改成999,则仅仅是改变了深蓝色区域的值。克隆出来的两个对象p1、p2中,wallet指向还是同一块内存,所以,通过p2对象修改wallet,也会影响到p1对象的wallet。
我们管这种更改克隆对象会影响原对象内容的拷贝方式称为 浅拷贝。 而将克隆后的两个对象,无论怎么修改,都不会影响另一个对象的拷贝方式,即拷贝后的两个对象互不影响的拷贝方式,称为 深拷贝。
3 如何实现深拷贝
还是以刚刚的例子为例,我们的目的就是,让拷贝后的两个对象p1、p2互不影响,则 只要让拷贝后的两个对象各自的wallet也拷贝一份,不就能解决问题了!
示意图如下:
⭐️ 实现步骤:
- 让Wallet类实现Clonable接口,并实现clone方法。
- 修改Person类实现的clone方法,实现person的克隆,同时也要克隆一份Wallet对象。
4 深拷贝实现完整代码
按照上述步骤,实现Person类的深拷贝,完整代码如下:
class Person implements Cloneable {
private String name;
private Wallet myMoney;
public Wallet getMyMoney() {
return myMoney;
}
public void setMyMoney(Wallet myMoney) {
this.myMoney = myMoney;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 实现克隆Person对象
@Override
protected Person clone() {
Person p = null;
try {
p = (Person) super.clone();
myMoney = this.myMoney.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
class Wallet implements Cloneable {
private int money;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
// 实现克隆Wallet对象
@Override
protected Wallet clone() {
Wallet w = null;
try {
w = (Wallet) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return w;
}
}
测试代码如下:
public class CloneDemo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("黄小黄");
Wallet wallet = new Wallet();
wallet.setMoney(100);
p1.setMyMoney(wallet);
Person p2 = p1.clone();
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
System.out.println("p1 = p2 ? " + (p1 == p2));
System.out.println("===修改p2的wallet属性为999===");
wallet.setMoney(999);
p2.setMyMoney(wallet);
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
}
}
测试结果如下:
写在最后
🌟以上便是本文的全部内容啦,后续内容将会持续免费更新,如果文章对你有所帮助,麻烦动动小手点个赞 + 关注,非常感谢 ❤️ ❤️ ❤️ !
如果有问题,欢迎私信或者评论区!
共勉:“你间歇性的努力和蒙混过日子,都是对之前努力的清零。”