Java拷贝(赋值、浅拷贝、深拷贝)


拷贝

在这里插入图片描述

直接赋值

直接赋值的方式没有生产新的对象,只是生新增了一个对象引用
在这里插入图片描述

浅拷贝

如果原型对象的成员变量是值类型,将复制一份给克隆对象,也就是说在堆中拥有独立的空间;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。换句话说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
在这里插入图片描述
在这里插入图片描述

实现方式

被复制类需要实现 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='我是张三'}

原因:

  1. String 和 Integer 等包装类都是不可变的对象。
  2. 当需要 修改 不可变对象的值时,需要在内存中生成一个新的对象来存放新的值,然后将原来的引用指向新的地址。
  3. 所以在这里我们修改了 person1 对象的 name 属性值,person1 对象的 name 字段指向了内存中新的 name 对象,但是我们并没有改变 person 对象的 name 字段的指向,所以 person 对象的 name 还是指向内存中原来的 name 地址,也就没有变化

深拷贝

相对于浅拷贝,深拷贝是一种完全拷贝,无论是值类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象。拷贝对象和被拷贝对象没有任何关系,互不影响。
在这里插入图片描述

实现方式

  1. 实现Cloneable接口
  2. 实现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 接口方式也可以实现深拷贝,而且这种方式还可以解决多层克隆的问题,多层克隆就是引用类型里面又有引用类型,层层嵌套下去。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的对象赋值拷贝有两种方式:深拷贝浅拷贝浅拷贝是指,仅仅拷贝对象的引用,而不是对象本身。也就是说,对于原始对象和拷贝对象来说,它们共享同一个内存地址,因此它们的改变会互相影响。 深拷贝是指,拷贝对象本身的所有内容,包括对象中的引用类型。也就是说,对于原始对象和拷贝对象来说,它们有着不同的内存地址,它们的改变不会互相影响。 Java中的浅拷贝可以使用Object类的clone()方法实现,而深拷贝需要自己实现。下面是一个深拷贝的例子: ```java import java.io.*; public class DeepCopy implements Serializable { private String name; private int age; private Person person; public DeepCopy(String name, int age, Person person) { this.name = name; this.age = age; this.person = person; } public DeepCopy deepCopy() throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (DeepCopy) ois.readObject(); } public static void main(String[] args) { Person person = new Person("Tom"); DeepCopy original = new DeepCopy("David", 20, person); try { DeepCopy copy = original.deepCopy(); System.out.println("original == copy? " + (original == copy)); // false System.out.println("original.person == copy.person? " + (original.person == copy.person)); // false } catch (Exception e) { e.printStackTrace(); } } } class Person implements Serializable { private String name; public Person(String name) { this.name = name; } } ``` 在这个例子中,DeepCopy类包含了一个Person类的引用类型。在deepCopy()方法中,我们将对象序列化成字节流,然后再将字节流反序列化成对象,这样就实现了深拷贝。在main()方法中,我们分别创建了原始对象和拷贝对象,并且打印了它们的地址和对象中引用类型的地址,可以看到它们都是不同的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值