1.基本类型与引用类型
- 基本类型:基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型
byte、short、int、long、float、double。 - 引用类型:包括类、接口、数组、枚举等。
Java 将内存空间分为堆和栈。基本类型直接在栈中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆中,通过栈中的引用指向堆中存放的数据。
2.浅拷贝
浅拷贝实现很简单,只要让需要拷贝的类实现Cloneable接口并重写clone方法即可。浅拷贝中只是将基本类型复制到栈空间中,引用类型的数据仅仅在栈中创建了对象名,对象名还是指向堆中的原数据,所以在改变引用对象时,浅复制过去的引用类型都会改变,因为引用类型在堆中为同一个。
专业类:
public class Major {
public Major(String name, int id) {
this.name = name;
this.id = id;
}
private String name;
private int id;
...省略get、set、toString方法...
}
学生类:
public class Student implements Cloneable{
public Student(int id, String name, Major m) {
this.id = id;
this.name = name;
this.m = m;
}
private int id;
private String name;
private Major m;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
...省略get、set、toString方法...
public static void main(String[] args) throws CloneNotSupportedException{
Major major = new Major("信息",11);
Student student1 = new Student(22,"李四",major);
Student student2 = (Student)student1.clone();
System.out.println("修改前1为"+student1.toString());
System.out.println("修改前2为"+student2.toString());
major.setId(33);
major.setName("城建");
student1.setId(44);
student1.setName("张三");
System.out.println("修改后1为"+student1.toString());
System.out.println("修改后2为"+student2.toString());
}
}
结果为:在修改了Student1中的Major对象后,发现Student2中的Major也会改变。
修改前1为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改前2为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改后1为Student{id=44, name=’张三’, m=Major{name=’城建’, id=33}}
修改后2为Student{id=22, name=’李四’, m=Major{name=’城建’, id=33}}
3.深拷贝
深拷贝完完全全的对一个对象进行了复制,无论是栈还是堆中内容都会改变。在改变原对象后拷贝后的对象不会发生改变。实现深拷贝的方式如下:
- 让每个引用类型属性内部都重写clone()方法。如Student中有引用类型Major,所以Major需要实现Cloneable接口并重写clone()方法。
专业类修改为:
public class Major implements Cloneable{
public Major(String name, int id) {
this.name = name;
this.id = id;
}
private String name;
private int id;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
...省略方法...
}
学生类重写的clone方法变成:
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.m=(Major) m.clone();
return student;
}
结果为:
修改前1为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改前2为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改后1为Student{id=44, name=’张三’, m=Major{name=’城建’, id=33}}
修改后2为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
- 序列化实现深拷贝:序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。如果嵌套多层引用类型,建议使用反序列化方式实现深拷贝。
每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。
Student需要完成深拷贝,由于Student中存在引用类型Major,所以在Student、Major中实现Serializable接口,并在Student类中添加深拷贝方法(这种方法不需要在实现Cloneable接口并重写clone()方法了)。主要的方法体为:
//序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
专业类:
public class Major implements Serializable{
public Major(String name, int id) {
this.name = name;
this.id = id;
}
private String name;
private int id;
...省略get、set、toString方法...
}
学生类:
public class Student implements Serializable {
public Student(int id, String name, Major m) {
this.id = id;
this.name = name;
this.m = m;
}
private int id;
private String name;
private Major m;
//深拷贝方法
public Object deepClone() throws Exception{
//序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
...省略方法...
}
本人小白一个,如果有什么问题还请您在评论区留言!感谢