浅谈Java中的复制

相信大家平时写代码经常会需要复制一份属性或对象,那么今天,我们就来讨论一下Java中常用的复制方法。

一、复制基本类型

对于基本类型来说,我们可以使用=号来进行复制,例如:

        int a = 25;
        int b = a;

上面b复制了a的值为25

二、复制对象

1、等号方法

创建一个Address类,它拥有一个addName属性

public class Address implements Cloneable {
    private String addName;

    public String getAddName() {
        return addName;
    }

    public void setAddName(String addName) {
        this.addName = addName;
    }

    @Override
    public String toString() {
        return "Address{" +
                "地址='" + addName + '\'' +
                '}';
    }

}

如何复制一份address对象呢,有的同学说,这还不容易吗,也用=号复制就行:

        Address address = new Address();
        address.setAddName("陋室");
        Address addressC = address;
        System.out.println("复制前:" + address.toString());
        System.out.println("复制后:" + addressC.toString());

运行结果:

可以看到成功复制了address,这时候我们改变address对象的值:

        address.setAddName("南阳诸葛庐");
        System.out.println("属性改变");
        System.out.println("复制前:" + address.toString());
        System.out.println("复制后:" + addressC.toString());

运行结果:
在这里插入图片描述
我们想改变address对象的值,却把addressC的值一同改变了,这是为什么呢?

原因:使用等号复制时,对于值类型来说,彼此之间的修改操作是相对独立的,而对于引用类型来说,因为复制的是引用对象的内存地址,所以修改其中一个属性值,另一个值也会跟着变化。

为了解决属性变化的问题,我们可以采用克隆方式来复制对象

2、克隆方法

浅克隆:克隆一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

深克隆:克隆一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否需要进行克隆。

实现克隆的步骤(分三步):

  • 对象的类实现Cloneable接口
  • 覆盖Object类的clone()方法(覆盖clone()方法,访问修饰符设为public,默认是protected)
  • 在clone()方法中调用super.clone()
(1)浅克隆

创建一个Student实体类,拥有姓名、年龄、地址三个属性,地址属性为上面Address类的对象,按照上述步骤实现Cloneable接口并覆盖clone方法

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 String toString() {
        return "Student{" +
                "姓名='" + name + '\'' +
                ", 年龄=" + age +
                "," + address.toString() +
                '}';
    }


    public Student clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        return student;
    }
}

在Main函数测试:

        Student student = new Student();
        student.setName("秦始皇");
        student.setAge(40);
        Address address = new Address();
        address.setAddName("秦始皇陵");
        student.setAddress(address);

        Student studentC = student.clone();
        System.out.println("克隆前:" + student.toString());
        System.out.println("克隆后:" + studentC.toString());

运行结果:
在这里插入图片描述

可以发现我们成功克隆了Student类,下面改变Student的属性:

        student.setName("嬴政");
        student.setAge(25);
        address.setAddName("咸阳阿房宫");
        System.out.println("属性发生改变");

        System.out.println("克隆前:" + student.toString());
        System.out.println("克隆后:" + studentC.toString());

运行结果:
在这里插入图片描述
可以看到studentC对象的姓名和年龄属性并没有随着student对象的改变而改变,但是Address属性仍指向同一个引用,所以我们需要使用深克隆来进行完整的复制

(2)深克隆

我们对Student类和Adress类进行一些改造
修改Student类的clone方法,同时克隆address对象

public Student clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.setAddress(student.getAddress().clone());
        return student;
    }

让Address类实现克隆方法

public class Address implements Cloneable {
    private String addName;

    public String getAddName() {
        return addName;
    }

    public void setAddName(String addName) {
        this.addName = addName;
    }

    @Override
    public String toString() {
        return "Address{" +
                "地址='" + addName + '\'' +
                '}';
    }


    public Address clone() throws CloneNotSupportedException {
        Address address = (Address) super.clone();
        return address;
    }
}

Main函数测试:

        Student student = new Student();
        student.setName("秦始皇");
        student.setAge(40);
        Address address = new Address();
        address.setAddName("秦始皇陵");
        student.setAddress(address);

        Student studentC = student.clone();
        System.out.println("克隆前:" + student.toString());
        System.out.println("克隆后:" + studentC.toString());

        student.setName("嬴政");
        student.setAge(25);
        address.setAddName("咸阳阿房宫");
        System.out.println("属性发生改变");

        System.out.println("克隆前:" + student.toString());
        System.out.println("克隆后:" + studentC.toString());

运行结果:
在这里插入图片描述
可以看到,student被完全克隆成studentC,并且它的属性不随着原对象的改变而改变

3、流处理方式

我们也可以使用流处理来复制对象

        Student student = new Student();
        student.setName("秦始皇");
        student.setAge(40);
        Address address = new Address();
        address.setAddName("秦始皇陵");
        student.setAddress(address);

        //将对象写到流里
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(byteOut);
        objOut.writeObject(student);
        //从流里读出来
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream objInput = new ObjectInputStream(byteIn);
        Student studentC = (Student) objInput.readObject();

        System.out.println("克隆前:" + student.toString());
        System.out.println("克隆后:" + studentC.toString());

        student.setName("嬴政");
        student.setAge(25);
        address.setAddName("咸阳阿房宫");
        System.out.println("属性发生改变");

        System.out.println("克隆前:" + student.toString());
        System.out.println("克隆后:" + studentC.toString());

运行结果:
在这里插入图片描述
可以看到结果与深克隆一致。

三、小结

复制的方式有很多,我们应该结合场景灵活地选取方法来实现复制,如果不改变对象属性,我们可以直接用等号复制;对象只有基本类型的属性,我们可以使用浅克隆方式;对象引用属性层数浅,可以使用深克隆方式;对象引用属性层数深,建议使用对象流处理方式。

参考链接:
https://my.oschina.net/u/4391811/blog/3252764
https://www.cnblogs.com/Qian123/p/5710533.html
https://www.cnblogs.com/liqiangchn/p/9465186.html

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java,可以使用以下两种方式复制类: 1. 实现Cloneable接口并重写clone()方法 该方法利用Java的原型模式来创建新对象,并将原对象的属性复制到新对象。 示例代码: ``` public class MyClass implements Cloneable { private int num; private String str; // 构造方法和其他方法 // 重写clone()方法 public MyClass clone() throws CloneNotSupportedException { return (MyClass) super.clone(); } } ``` 使用时,调用对象的clone()方法即可创建新的对象: ``` MyClass obj1 = new MyClass(); MyClass obj2 = obj1.clone(); // 复制对象 ``` 2. 使用序列化和反序列化 该方法利用Java的序列化和反序列化来实现对象的复制。 示例代码: ``` public class MyClass implements Serializable { private int num; private String str; // 构造方法和其他方法 // 序列化方法 public byte[] serialize() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(this); return out.toByteArray(); } // 反序列化方法 public static MyClass deserialize(byte[] data) throws IOException, ClassNotFoundException { ByteArrayInputStream in = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(in); return (MyClass) ois.readObject(); } } ``` 使用时,先将对象序列化为字节数组,再将字节数组反序列化为新对象: ``` MyClass obj1 = new MyClass(); byte[] data = obj1.serialize(); // 序列化对象 MyClass obj2 = MyClass.deserialize(data); // 反序列化对象 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值