为什么要克隆?
为什么要克隆对象?直接通过new一个对象不就好了?
克隆的对象可能包含一些已经修改的属性,而通过new出来的对象的属性都是初始化的值。
今天介绍一下两种不同的克隆方法:浅克隆 和 深克隆。
通过实现Cloneable接口克隆
浅克隆
1、被复制的类需要实现Cloneable接口(该接口是标记接口,没有任何方法)
2、重写clone()方法,访问修饰符设为public。
public class CloneTest {
public static void main(String[] args) {
// 创建User对象
User user = new User();
user.setName("xffjs");
user.setAddress("小飞博客");
// 克隆对象
User cloneUser = user.clone();
System.out.println(user);
System.out.println(cloneUser);
}
}
class User implements Cloneable {
String name;
String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
@Override
public User clone() {
User user = null;
try {
user = (User) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return user;
}
}
// 输出结果:
User{name='xffjs', address='小飞博客'}
User{name='xffjs', address='小飞博客'}
深克隆
我们把User类里面的Address类型改成类,然后在克隆完成以后再修改Address类里面的addr。
public class CloneTest {
public static void main(String[] args) {
// 创建User对象
User user = new User();
user.setName("xffjs");
// 创建Address对象并set给user
Address address = new Address();
address.setAddr("小飞博客");
user.setAddress(address);
// 克隆对象
User cloneUser = user.clone();
// 修改Address
address.setAddr("小飞");
System.out.println(user);
System.out.println(cloneUser);
}
}
class User implements Cloneable {
String name;
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
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
@Override
public User clone() {
User user = null;
try {
user = (User) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return user;
}
}
class Address implements Cloneable {
String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" +
"addr='" + addr + '\'' +
'}';
}
@Override
public Address clone() {
Address address = null;
try {
address = (Address) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return address;
}
}
// 输出结果:
User{name='xffjs', address=Address{addr='小飞'}}
User{name='xffjs', address=Address{addr='小飞'}}
可以发现上面修改了user的addr,但是cloneUser也跟着改变了。
原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
我们修改user里面的clone方法如下:
@Override
public User clone() {
User user = null;
try {
// 浅复制
user = (User) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
// 深复制
user.address = (Address) address.clone();
return user;
}
// 输出结果:
User{name='xffjs', address=Address{addr='小飞'}}
User{name='xffjs', address=Address{addr='小飞博客'}}
总结
1、浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象。
简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
2、深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。
通过实现Serializable接口克隆
public class CloneTest {
public static void main(String[] args) {
// 创建User对象
User user = new User();
user.setName("xffjs");
// 创建Address对象并set给user
Address address = new Address();
address.setAddr("小飞博客");
user.setAddress(address);
// 克隆对象
User cloneUser = user.myclone();
// 修改Address
address.setAddr("小飞");
System.out.println(user);
System.out.println(cloneUser);
}
}
class User implements Serializable {
// 最好是显式声明ID
private static final long serialVersionUID = 1L;
String name;
Address address;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
public User myclone() {
User user = 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);
user = (User) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return user;
}
}
class Address implements Serializable {
// 最好是显式声明ID
private static final long serialVersionUID = 1L;
String addr;
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" +
"addr='" + addr + '\'' +
'}';
}
}
打赏
当前共有 0 条评论