Skip to content

Clone 方法

概述

对象复制在 Java 中不是 obj.copy() 这么简单。Object 的默认 clone()浅克隆——如果你有一个包含嵌套引用的对象,克隆后的对象和原对象会共享内部引用。这是一个经常被忽视的坑。

基本概念

Cloneable 接口

Cloneable 是一个标记接口,告诉 JVM 这个类可以被克隆:

java
class MyClass implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

为什么要重写?

Object.clone() 是 native 方法,直接调用会按位复制所有字段。对于基本类型没问题,但引用类型会共享地址。

代码示例

基本克隆

java
public class CloneBasicDemo {

    static class Person implements Cloneable {
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        protected Person clone() throws CloneNotSupportedException {
            return (Person) super.clone();
        }

        public String getName() { return name; }
        public int getAge() { return age; }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person("张三", 25);
        Person p2 = p1.clone();

        System.out.println("p1: " + p1.getName());
        System.out.println("p2: " + p2.getName());
        System.out.println("p1 == p2: " + (p1 == p2)); // false:不同对象
        System.out.println("p1.name == p2.name: " + (p1.getName() == p2.getName())); // true:共享字符串
    }
}

浅克隆问题

java
public class ShallowCloneProblemDemo {

    static class Address {
        String city;
        Address(String city) {
            this.city = city;
        }
    }

    static class Person implements Cloneable {
        String name;
        Address address;  // 引用类型

        public Person(String name, Address address) {
            this.name = name;
            this.address = address;
        }

        @Override
        protected Person clone() throws CloneNotSupportedException {
            return (Person) super.clone(); // 默认浅克隆
        }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("北京");
        Person p1 = new Person("张三", addr);
        Person p2 = p1.clone();

        // 两个对象共享同一个 Address
        System.out.println("修改前 p1.city: " + p1.address.city); // 北京
        System.out.println("修改前 p2.city: " + p2.address.city); // 北京

        // 修改 p2 的地址,p1 也受影响!
        p2.address.city = "上海";
        System.out.println("修改后 p1.city: " + p1.address.city); // 上海 ❌
        System.out.println("修改后 p2.city: " + p2.address.city); // 上海
    }
}

深克隆

对于有引用类型字段的对象,需要手动克隆每个引用:

java
public class DeepCloneDemo {

    static class Address implements Cloneable {
        String city;

        Address(String city) {
            this.city = city;
        }

        @Override
        protected Address clone() throws CloneNotSupportedException {
            return new Address(this.city); // 新建对象
        }
    }

    static class Person implements Cloneable {
        String name;
        Address address;

        public Person(String name, Address address) {
            this.name = name;
            this.address = address;
        }

        @Override
        protected Person clone() throws CloneNotSupportedException {
            Person p = (Person) super.clone();
            p.address = this.address.clone(); // 深克隆:克隆嵌套对象
            return p;
        }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("北京");
        Person p1 = new Person("张三", addr);
        Person p2 = p1.clone();

        // 修改 p2 的地址,p1 不受影响
        p2.address.city = "上海";
        System.out.println("p1.city: " + p1.address.city); // 北京 ✅
        System.out.println("p2.city: " + p2.address.city); // 上海
    }
}

深克隆 vs 浅克隆

类型描述引用类型字段
浅克隆只复制对象本身引用地址共享
深克隆递归复制所有嵌套对象完全独立

序列化实现深克隆

对于复杂对象图,可以用序列化实现深克隆:

java
public class SerializationCloneDemo {

    static class DeepCloneUtil {
        public static <T extends Serializable> T deepClone(T obj) {
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(obj);

                ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bis);
                return (T) ois.readObject();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

注意事项

  1. 实现 Cloneable:不实现这个接口,clone() 会抛 CloneNotSupportedException
  2. 重写 clone():调用 super.clone() 享受 JVM 的 native 优化
  3. 深克隆:对于包含引用类型字段的类,需要手动递归克隆
  4. 替代方案:如果不需要高性能,可以用序列化实现深克隆
  5. 不可变字段final 引用且不可变对象(如 String)不需要深克隆

基于 VitePress 构建