=,浅克隆,深克隆区别
1、=
public class Student{
int studentNo;
}
public class Client{
public static void main(String[] args){
Student stu1 = new Student();
stu1.studentNo = 12345;
Student stu2 = stu1; // 引用赋值
System.out.println(stu2 == stu1); // 输出true。stu1与stu2拥有完全相同的值,与克隆类似
// 输出stu2的学号
System.out.println(stu2.studentNo); // 输出12345
stu1.studentNo = 54321;
System.out.println(stu2.studentNO) // 输出54321
// 因为是引用赋值,stu2把stu1的引用地址复制了一份,指向堆内存中的同一个地址,所以stu1改变堆中的值后,stu2也会改变
}
}
2、浅克隆
只复制基本数据类型,对于对象里的其他引用类型的数据还是复制引用地址。浅克隆要实现Cloneable接口,否则会报错。
public class Person{
public int age;
}
public class Student implements{
public int studentNo;
public Person person;
public Object clone() throw CloneNotSupportedException{
return super.clone();
}
}
public class Client{
public static void main(String[] args){
Student stu1 = new Student();
Person person = new Person();
person.age = 18;
stu1.studentNo = 12345;
stu1.person = person;
Student stu2 = (Student) stu1.clone(); // 克隆完以后,stu2与stu1数据一样。但此时stu2中的person对象与stu1中的应用对象只想同一快堆内存,stu1中的person对象中的值改变,stu2也会随着改变,但stu2中studentNo不会在随着stu1中studentNo的值的改变而改变。
System.out.println(stu1 == stu2); // false。说明stu1与stu2不在指向同一块堆空间
System.out.println(stu2.studentNo); //12345
stu1.studentNo = 54321;
System.out.println(stu2.studentNo); //12345。stu1中基本数据类型的改变不会影响stu2。这是浅克隆与=的区别。
stu1.person.age = 80;
System.out.println(stu2.person.age); // 80。stu1中引用数据类型变化,还是会影响stu2。这需要深克隆解决
}
}
3、深克隆
深克隆有两种实现方式,一种是重写clone方法,一种是序列化(Servil)
(1)重写clone方法
public class Animal{
public int leg;
}
public class Person implements Cloneable{
public int age;
public Animal animal;
@Override
public Object clone(){
return super.clone();
}
}
public class Student implemts Cloneable{
public int studentNo;
public Person person;
@Override
public Object clone(){
this.clone = person.clone(); // 手动克隆引用对象
return super.clone():
}
}
public class Clinet{
public static void main(String[] args){
Person person = new Person();
Student student = new student();
Animal animal = new Animal();
animal.leg = 2;
person.age = 18;
person.animal = animal;
student.studentNo = 12345;
student.person = person;
Person clone = (Person) person.clone();
System.out.println(clone.person.age); // 输出18
person.age = 20;
Syste.out.println(clone.person.age); // 输出18。
System.out.println(clone.person.animal.leg); // 输出2
anima.leg = 4;
System.out.println(clone.person.animal.leg); // 输出4
}
}
需要手动克隆每一个应用对象,若要clone的对象里有一个引用对象,这个引用对象里还有另一个对象,那么要递归的编写每一个对象的clone方法,否则只能clone重写了clone方法的对象。
(2)实现serializable接口
public class Animal implements Serializable{
private static final long serialVersionUID = 1L;
public int leg;
}
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
public int age;
public Animal animal;
}
public class Studeng implements Serializable{
private static final long serialVersionUID = 1L;
public int studetNo;
public Person person;
@Override
public Object clone(){
Student stu = null;
try{
// 对象序列化
ByteArrayOutputStream baos = new ByteArrayOutputStram();
ObjectOutputStream oos = new ByteArrayOutPutStram(baos);
oos.writeObject(this);
baos.close();
oos.close();
// 对象反序列化
ByteArrayInputStram bais = new ByteArrayInputStream();
ObjectInputStram ois = new ObjectInputStram(bais);
stu = (Student)ois.readObject();
bais.close();
ois.close();
// close操作最好在finall中。流打开后,后面操作若触发异常,流就不会关闭了
}catch(Exception e){
}
return stu;
}
}
public class Client{
public static void main(String[] args){
Animal animal = new Aniaml();
animal.leg = 2;
Person person = new Person();
person.age = 18;
person.animal = anliml();
Student student = new Student();
student.studentNo = 12345;
student.person = person;
Student clone = (Student) student.clone();
animal.leg = 4;
System.out.println(student.person.animal.leg); // 输出4
System.out.println(clone.person.animal.leg); // 输出2
person.age = 20;
System.out.println(student.person.age); // 输出20
System.out.println(clone.person.age); // 输出18
}
}
实现Serializable接口的方式要比重写clone方法的方式更加简洁,实现Cloneable的方法,需要为每一个对象都重写clone方法。实现Serializable接口只需要重写一个clone方法即可。