目录
对象克隆
在讨论对象克隆之前, 可能会有人说 , 不能直接new一个吗?为什么要克隆
首先关于这个问题, 直接new一个对象, 这个对象里面包含的成员变量是null的 , 那问题又来了, 我不能去一个个set对象的值吗? 当然可以, 但这样麻烦, 对象克隆采用的是native方法,效率更高
那么该怎样去克隆呢? 克隆又分为浅克隆和深克隆
首先这样的方式不叫克隆 (这种只能叫做引用复制):
Object obj1 = new Object();
Object obj2 = obj1;
怎样区分是浅克隆还是深克隆呢?
在java中, 数据类型分为基本类型和引用类型 , 在复制数据的过程中, 基本类型的值会直接被复制过去, 但是引用类型只能复制引用的地址 , 所以深浅克隆的区别就是是否将引用类型所指向的变量也复制了
浅克隆
先来看浅克隆 , 有两种实现方式, 一是重写Object类中的clone() 方法, 二是spring 框架中提供 BeanUtils.copyProperties(source,target); 这里我们演示第一种
//需实现Cloneable接口
public class Person implements Cloneable {
int num;
String name;
public Person() { }
public Person(int num, String name) {
this.num = num;
this.name = name;
}
//重写Object中的clone()方法, 实现克隆
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
return person;
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(100,"jim");
Person p2 = p1.clone(); //调用克隆方法
System.out.println(p1==p2);
}
以上输出结果当然为 false , 因为克隆后虽然包含数据相同, 但仍旧是两个对象, 采用 == 的方式比较, 输出的是两个对象的引用地址是否相同 , 当然为 false
当然在这里肯定有人会提出这样一个问题 : String不是引用类型吗? 怎么值跟着复制了
首先我们回想String的特点, 引用类型没错, 但是String底层的char数组是采用了 final修饰的, 被final修饰的我们叫做常量, 也就是不可改变的, 所以说 , String虽然是引用类型, 但是它的值一旦确定就不能再改变了, 所以值会跟着复制
深克隆
深克隆会将引用类型所指向对象中包含的数据一同复制, 这里也需实现Cloneable接口
先设计一个Address类
//实现Cloneable接口
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 + '\'' +
'}';
}
// 重写clone()方法
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
}
接着是person类
//实现Cloneable接口
public class Person implements Cloneable{
int num;
String name;
//在Person中关联Address对象
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
// 由于会导致篇幅过长, 此处省略属性的get和set方法
//重写clone()方法实现深度克隆
@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{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
test类
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
//先new一个Address类对象并设置值
Address address = new Address();
address.setAddress("成都");
// new 一个Person类对象并设置值(将address属性加入)
Person p1 = new Person(100,"jim");
p1.setAddress(address);
//克隆
Person p2 =p1.clone();
p2.setName("tom");
//改变address所指向对象的值,如果深克隆成功,这个值即使改变也影响不了p2
address.setAddress("西安");
System.out.println("p1:"+p1);
System.out.println("p2:"+p2);
/*
* 结果 : p1:Person{num=100, name='jim', address=Address{address='西安'}}
* p2:Person{num=100, name='tom', address=Address{address='成都'}}
* 深克隆成功
* */
}
}
这就是深克隆, 连同引用对象中包含的值一起复制, 在上述例子中 , 如果是浅克隆, 仅仅复制了引用, 引用一旦发生改变, 原对象和克隆的对象中的address属性都会发生改变(如果浅克隆上述例子p1和p2中的address都会为"西安")
多层克隆
了解了深克隆, 那么有人会想到, 如果对象中套对象, 对象中再套对象, 这样一直连环套该怎样解决呢 ?
最为简单粗暴的一种方式就是 : 手动一层一层克隆, 这样当然也可以, 但是这里我们介绍一种更为简便的方式 : 序列化 , 当然简单的深克隆也是可以去使用序列化这种方式的
序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable 接口,否则无法实现序列化操作。
Address类
//实现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 + '\'' +
'}';
}
}
Person类
//实现Serializable接口
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;
}
//省略get和set方法
//自定义克隆方法
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 +
'}';
}
}
测试不变, 不过这里我们克隆使用的是自己定义的myClone()方法 ,使用流的方式完成
关于对象克隆就说到这里, 感谢阅读.