Java中的对象克隆

本文详细介绍了Java中对象克隆的原理与实现方法,包括浅克隆与深克隆的区别及应用场景,通过实例展示了如何利用Cloneable接口和序列化方式完成对象的复制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在 Java 语言中,我们说两个对象是否相等通常有两层含义:

1。对象的内容是否相等,通常使用到对象的 equals(Object o) 函数;
2。引用的地址是否相同,使用运算符 == 比较即可。

当两个对象通过赋值符号 = 赋值时,表明这两个对象指向了内存中同一个地址,所以改变其中一个对象的内容,也就间接地改变了另一个对象的内容。有时候,我们需要从一个已经存在的对象重新拷贝一份出来,并且不仅这两个对象内容相等,在内存中存在两个独立的存储地址,互不影响,这时,就需要用到 Java 中的克隆机制。

Cloneable
通过 Cloneable 接口可以很轻松地实现 Java 对象的克隆,只需要 implements Cloneable 并实现 Object 的 clone() 方法即可。注意这里对象实现的是 Object 类的 clone() 方法,因为 Cloneable 是一个空接口:

public interface Cloneable {
}

从源码注释中可以看出,需要实现 Object 类中的 clone() 方法(注意:clone() 函数是一个 native 方法,同时抛出了一个异常):

 protected native Object clone() throws CloneNotSupportedException;

测试代码如下(注意:我们在 User 对象中重写了 equals() 函数):

public class User implements Cloneable {

    private String username;
    private String password;

    public User(String username, String password) {
        super();
        this.username = username;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((password == null) ? 0 : password.hashCode());
        result = prime * result
                + ((username == null) ? 0 : username.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        User user = (User)obj;
        if(username.equals(user.username) && password.equals(user.password)) {
            return true;
        }
        return false;
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        User userOne , userTwo, userThree;
        userOne = new User("username", "password");
        userTwo = userOne;
        userThree = (User) userOne.clone();

        System.out.println(userTwo == userOne); //true
        System.out.println(userTwo.equals(userOne));//true
        System.out.println(userThree == userOne);//false
        System.out.println(userThree.equals(userOne));//true
    }
}

测试结果显示,通过 clone() 函数,我们成功地从 userOne 对象中克隆出了一份独立的 userThree 对象。
浅克隆与深克隆
谈此之前,我们先看一个例子,定义一个名为 Company 的类,并添加一个类型为 User 的成员变量:

public class Company implements Cloneable {

    private User user;
    private String address;

    public Company(User user, String address) {
        super();
        this.user = user;
        this.address = address;
    }

    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
//  protected Object clone() throws CloneNotSupportedException {
//      Company company = (Company) super.clone();
//       company.user = (User) company.getUser().clone();
//       return company;
//  }
    @Override 
    public boolean equals(Object obj) {
        Company company = (Company)obj;
        if(user.equals(company.getUser()) && address.equals(company.getAddress())) {
            return true;
        } 
        return false;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Company companyOne, companyTwo, companyThree;
        companyOne = new Company(new User("username", "password"), "上海市普陀区");
        companyTwo = companyOne;
        companyThree = (Company)companyOne.clone();

        System.out.println(companyOne == companyTwo);
        System.out.println(companyOne.equals(companyTwo));

        System.out.println(companyOne == companyThree);
        System.out.println(companyOne.equals(companyThree));

        System.out.println(companyThree.getUser() == companyOne.getUser());
        System.out.println(companyThree.getUser().equals(companyOne.getUser()));

    }
}

问题来了,companyThree 与 companyOne 中的 User 是同一个对象!也就是说 companyThree 只是克隆了 companyOne 的基本数据类型的数据,而对于引用类型的数据没有进行深度的克隆。也就是俗称的浅克隆。

浅克隆:顾名思义,就是很表层的克隆,只克隆对象自身的引用地址;
深克隆:也称“N层克隆”,克隆对象自身以及对象所包含的引用类型对象的引用地址。

这里需要注意的是,对于基本数据类型(primitive)和使用常量池方式创建的String 类型,都会针对原值克隆,所以不存在引用地址一说。当然不包括他们对应的包装类。

所以使用深克隆就可以解决上述 Company 对象克隆过后两个 user 对象的引用地址相同的问题。我们修改一下 Company 类的 clone() 函数:

    protected Object clone() throws CloneNotSupportedException {
        Company company = (Company) super.clone();
         company.user = (User) company.getUser().clone();
         return company;
    }

再运行测试代码,就能得到 companyThree.getUser()==companyOne.getUser() 为 false 的结果了。
Serializable实现

通过上述介绍,我们知道,实现一个对象的克隆,需要如下几步:
1。对象所在的类实现 Cloneable 接口;
2。重写 clone() 函数,如果包涵引用类型的成员变量,需要使用深克隆。
如果对象不包含引用类型成员或者数量少的话,使用 Cloneable 接口还能接受,但当对象包含多个引用类型的成员,同时这些成员又包含了引用类型的成员,那层层克隆岂不是相当繁琐,并且维护不便?所以,这里介绍一种更加方便的实现方式,使用 ObjectOutputStream 和 ObjectOutputStream 来实现对象的序列化和反序列化

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class People implements Serializable {

    private String name;
    private int age;
    public People() {
        this.name = "wangwenhao";
        this.age = 26;
    }

    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 static void main(String[] args) throws IOException, ClassNotFoundException {
        People people = new People();
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        FileOutputStream fos = new FileOutputStream("people.out");
        oos = new ObjectOutputStream(fos);
        oos.writeObject(people);
        oos.close();
        People anotherPeople = null;
        FileInputStream fis = new FileInputStream("people.out");
        ois = new ObjectInputStream(fis);
        anotherPeople = (People) ois.readObject();
        System.out.println("name: " + anotherPeople.getName() + "  " + " age:  " + anotherPeople.getAge());
        ois.close();
    }
}

序列化一组对象,因为数组属于引用数据类型, 所以可以直接使用Object类型接收。下面是代码演示:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

class Person implements Serializable {
    private String name;
    private int age;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

public class SerDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person per[] = {new Person("张三", 30), new Person("李四", 31), new Person("王五", 32)};
        ser(per);
        Object o[] = dser();
        for(int i = 0; i < o.length; i++) {
            Person p = (Person)o[i];
            System.out.println(p);
        }

    }
    public static void ser(Object obj[]) throws IOException {
        File f = new File("D:" + File.separator + "test.txt");
        OutputStream out = new FileOutputStream(f);
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(obj);
        oos.close();
    }
    public static Object[] dser() throws IOException, ClassNotFoundException {
        File f = new File("D:" + File.separator + "test.txt");
        InputStream input = new FileInputStream(f);
        ObjectInputStream ois = new ObjectInputStream(input);
        Object obj[] = (Object[]) ois.readObject();
        ois.close();
        return obj;
    }
}

参考:http://www.tuicool.com/articles/rYbA3y

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值