拷贝
直接赋值
直接赋值的方式没有生产新的对象,只是生新增了一个对象引用
浅拷贝
如果原型对象的成员变量是值类型,将复制一份给克隆对象,也就是说在堆中拥有独立的空间;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。换句话说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
实现方式
被复制类需要实现 Cloneable 接口,重写 clone
方法即可,对 person 类进行改造,使其可以支持浅拷贝。
public class Person implements Cloneable {
private String name; // 姓名
private int age; // 年龄
private String email; // 邮件
private String desc; // 描述
private Person friend; // 朋友
/* 重写 clone 方法,需要将权限改成 public ,直接调用父类的 clone 方法就好了 */
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
特殊情况
public class PersonApp {
public static void main(String[] args) throws Exception {
// 初始化一个对象
Person person = new Person("张三",20,"123456@qq.com","我是张三");
// 复制对象
Person person1 = (Person) person.clone();
// 改变 person1 的属性值
person1.setName("我是张三的克隆对象");
// 修改 person age 的值
person1.setAge(22);
System.out.println("person对象:"+person);
System.out.println();
System.out.println("person1对象:"+person1);
}
}
// person对象:Person{name='张三', age=20, email='123456@qq.com', desc='我是张三'}
// person1对象:Person{name='我是张三的克隆对象', age=22, email='123456@qq.com', desc='我是张三'}
原因:
- String 和 Integer 等包装类都是不可变的对象。
- 当需要 修改 不可变对象的值时,需要在内存中生成一个新的对象来存放新的值,然后将原来的引用指向新的地址。
- 所以在这里我们修改了 person1 对象的 name 属性值,person1 对象的 name 字段指向了内存中新的 name 对象,但是我们并没有改变 person 对象的 name 字段的指向,所以 person 对象的 name 还是指向内存中原来的 name 地址,也就没有变化
深拷贝
相对于浅拷贝,深拷贝是一种完全拷贝,无论是值类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象。拷贝对象和被拷贝对象没有任何关系,互不影响。
实现方式
- 实现Cloneable接口
- 实现Serializable接口
实现Cloneable接口:
clone 方法不能是简单的调用super的clone()。
class Address{
String name;
String eCode;
public Address( String na, String eC;)
{
this.name = na;
this.eCode = eC;
}
}
public class Person implements Cloneable {
private String name; // 姓名
private int age; // 年龄
private String email; // 邮件
private Address address; //
private Person friend; // 朋友
/* 重写 clone 方法,需要将权限改成 public ,直接调用父类的 clone 方法就好了 */
@Override
public Object clone() throws CloneNotSupportedException {
Person newp = new Person();
newp.name = new String(this.name);
newp.age = this.age;
newp.email = new String(this.email);
newp.address = new Address(this.address.name, this.address.eCode);
return newp;
}
}
实现Serializable接口:
protected Son deepClone() throws IOException, ClassNotFoundException {
Son son=null;
//在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组中
//默认创建一个大小为32的缓冲区
ByteArrayOutputStream byOut=new ByteArrayOutputStream();
//对象的序列化输出
ObjectOutputStream outputStream=new ObjectOutputStream(byOut);//通过字节数组的方式进行传输
outputStream.writeObject(this); //将当前student对象写入字节数组中
//在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区
ByteArrayInputStream byIn=new ByteArrayInputStream(byOut.toByteArray()); //接收字节数组作为参数进行创建
ObjectInputStream inputStream=new ObjectInputStream(byIn);
son=(Son) inputStream.readObject(); //从字节数组中读取
return son;
}
多层克隆
Cloneable接口方式可以实现深拷贝。但是有个问题,如果引用数量或者层数太多了怎么办呢?不可能去每个对象挨个写clone()吧?那怎么办呢?借助序列化!!!。
实现 Serializable 接口方式也可以实现深拷贝,而且这种方式还可以解决多层克隆的问题,多层克隆就是引用类型里面又有引用类型,层层嵌套下去。