为什么要使用克隆
在业务中,有时候需要对对象进行克隆。例如Student对象,如果如下代码进行“克隆”,其实是引用重新赋值
Student stu1 = new Student();
stu1.setName("测试1");
stu1.setAge(23);
Student stu2 = stu1;
在上述代码中如果,对stu1,或者stu2的属性进行改变,两者的属性都会发生相同的改变。这样赋值,堆中还是只用一个Student对象,只不过在栈中存在stu1,stu2两个引用指向堆中的Student对象。
浅克隆
针对对象中的属性没有引用类型,都是基本数据类型。例如如下代码:
1.首先创建Student对象
创建Student对象,其中的属性是String类型的name,int类型的age。克隆需要继承Cloneable接口,这个接口是标识性接口。然后重写object中的clone方法。
package com.wx.qianKeLong;
public class Student implements Cloneable{
private String name;
private int age;
//private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// public Address getAddress() {
// return address;
// }
//
// public void setAddress(Address address) {
// this.address = address;
// }
@Override
public Object clone() throws CloneNotSupportedException {
Student student = null;
student = (Student) super.clone();
// student.address = (Address) address.clone();
return student;
}
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
// ", address=" + address +
'}';
}
}
2.创建测试类
在该类中,创建student对象,然后对student中的属性中name设置为wx,age设置为24。然后对student对象进行克隆,克隆后的对象为student1,然后student1对属性进行改变,name为wx2,age为25.最后将student的引用赋值给student3,student3对属性name和age进行修改
package com.wx.qianKeLong;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
//Address address= new Address();
//address.setName("China");
student.setName("wx");
student.setAge(24);
System.out.println("未修改之前的Student:"+student);
//student.setAddress(address);
Student student1 = (Student) student.clone();
//address.setName("American");
student1.setName("wx2");
student1.setAge(25);
Student student2 = student;
student2.setName("wx3");
student2.setAge(16);
System.out.println(student);
System.out.println(student1);
System.out.println(student2);
System.out.println("===============================");
}
}
结果
通过结果说明,student1通过克隆产生了新的对象,而student2只是引用的复制,当student2对属性进行修改,student的属性也发生修改。
深克隆-出现问题
1.创建Student类
创建Student类,属性为String类型的name,int类型的age,还有引用类型的 address。
package com.wx.qianKeLong;
public class Student implements Cloneable{
private String name;
private int age;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student student = null;
student = (Student) super.clone();
//student.address = (Address) address.clone();
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
2.创建Address类
创建Address类,属性为String类型的name
package com.wx.qianKeLong;
public class Address implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address{" +
"name='" + name + '\'' +
'}';
}
}
3.创建测试类
在测试类中,创建student对象,然后创建Address对象,对student对象中address属性进行赋值。然后对student对象进行克隆。克隆之后对address对象中name属性进行修改。如果是深克隆的话,对address对象进行修改,改变的话应该是student对象中的address属性,可是结果确实两个对象都发生修改,因此此克隆是浅克隆,没有对引用类型进行克隆,引用类型依然是引用的赋值。
package com.wx.qianKeLong;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
Address address= new Address();
address.setName("China");
student.setName("wx");
student.setAge(24);
student.setAddress(address);
Student student1 = (Student) student.clone();
address.setName("American");
System.out.println(student);
System.out.println(student1);
System.out.println("===============================");
}
}
结果
解决深克隆出现的问题
1.创建student对象
创建student对象,属性是String类型的name,int类型的age,Address类型的address
package com.wx.qianKeLong;
public class Student implements Cloneable{
private String name;
private int age;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student student = null;
student = (Student) super.clone();
student.address = (Address) address.clone();
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
2.创建address对象
创建Address类,实现Cloneable接口,重写clone()方法。
package com.wx.qianKeLong;
public class Address implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address{" +
"name='" + name + '\'' +
'}';
}
}
3.创建测试类
在测试类中,首先对student中的属性进行赋值,然后进行克隆。克隆之后的对象是student2.此时对address中的name属性进行修改,发现student中的address属性发生修改,而克隆之后的address属性没有修改。因为address属性是属于student的,在克隆后student1的address也进行克隆。此时address对name属性进行修改只会影响sutdent而不会影响student1.
package com.wx.qianKeLong;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
Address address= new Address();
address.setName("China");
student.setName("wx");
student.setAge(24);
student.setAddress(address);
Student student1 = (Student) student.clone();
address.setName("American");
System.out.println(student);
System.out.println(student1);
System.out.println("===============================");
}
}
结果
深克隆进阶-采用序列化和反序列话进行克隆
如果,在克隆中,多个类采用层层继承,那么在使用深克隆的时候就会产生在每一个类中都要重写clone方法,然后进行克隆。太过于繁琐。为了避免此问题,可以采用序列化和反序列化进行克隆。
1.创建Outer类
创建Outer类,属性为String类型的name,Inner类型的inner。然后创建getInstance方法,进行序列化和反序列化操作。
package com.wx.xuLieHuaKeLong;
import java.io.*;
public class Outer implements Serializable{
private String name;
private Inner inner;
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Outer{" +
"name='" + name + '\'' +
", inner=" + inner +
'}';
}
private static final long serialVersionUID = 123712831982744L;
public Outer getInstance(){
Outer outer = null;
try {
//将对象转化为流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//将流转化为对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
outer = (Outer) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return outer;
}
}
2.创建Inner类
创建inner类,实现serializable
package com.wx.xuLieHuaKeLong;
import java.io.Serializable;
public class Inner implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Inner{" +
"name='" + name + '\'' +
'}';
}
}
3.创建Test测试类
创建outer对象,然后getinstance进行克隆,克隆之后对inner属性进行修改,只影响outer对象中inner的属性,instance对象中的inner属性不发生变化。
package com.wx.xuLieHuaKeLong;
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.setName("zhangsan");
Inner inner = new Inner();
inner.setName("inner");
outer.setInner(inner);
Outer instance = outer.getInstance();
inner.setName("inner-copy");
instance.setName("lisi");
System.out.println(instance);
System.out.println(outer);
}
}
结果