Java 数组 clone() 详解:深入示例与最佳实践

发布: (2025年12月14日 GMT+8 21:49)
5 min read
原文: Dev.to

Source: Dev.to

什么是数组的 clone() 方法?

首先要记住的一点:对数组而言,clone() 执行的是 浅拷贝

浅拷贝 vs. 深拷贝:核心概念

  • 浅拷贝 – 创建一个新的数组对象,但其元素仍是指向原数组中相同对象的引用。通过一个数组修改可变对象时,另一个数组也会看到变化。
  • 深拷贝 – 完全独立地复制原数组 以及 其中的每个对象。对一个的修改不会影响另一个。

对于基本类型数组(int[]char[] 等),这个区别并不重要,因为数组存储的是实际值。此时 clone() 实际上会得到一个独立的副本。

对于对象数组(String[]Employee[] 等),数组存储的是引用,所以浅拷贝只会复制这些引用。

代码示例:动手实践

示例 1 – 基本类型数组

import java.util.Arrays;

public class CloneDemo {
    public static void main(String[] args) {
        int[] originalScores = {95, 87, 92, 64};
        int[] clonedScores = originalScores.clone();

        System.out.println("Original Array: " + Arrays.toString(originalScores));
        System.out.println("Cloned Array:   " + Arrays.toString(clonedScores));

        // Change the cloned array
        clonedScores[0] = 100;

        System.out.println("\nAfter modifying cloned array:");
        System.out.println("Original Array: " + Arrays.toString(originalScores)); // Still 95
        System.out.println("Cloned Array:   " + Arrays.toString(clonedScores));   // Now 100
    }
}

输出

Original Array: [95, 87, 92, 64]
Cloned Array:   [95, 87, 92, 64]

After modifying cloned array:
Original Array: [95, 87, 92, 64]
Cloned Array:   [100, 87, 92, 64]

示例 2 – 陷阱!(对象数组)

class Player {
    String name;
    int level;

    Player(String name, int level) {
        this.name = name;
        this.level = level;
    }
}

public class CloneGotcha {
    public static void main(String[] args) {
        Player[] originalTeam = {
            new Player("Alex", 10),
            new Player("Sam", 15)
        };
        Player[] clonedTeam = originalTeam.clone();

        System.out.println("Before change:");
        System.out.println("Original[0]: " + originalTeam[0].level); // 10
        System.out.println("Cloned[0]:   " + clonedTeam[0].level);   // 10

        // Modify the object referenced by the cloned array
        clonedTeam[0].level = 99;

        System.out.println("\nAfter changing clonedTeam[0].level to 99:");
        System.out.println("Original[0]: " + originalTeam[0].level); // Oops! 99!
        System.out.println("Cloned[0]:   " + clonedTeam[0].level);   // 99
    }
}

输出

Before change:
Original[0]: 10
Cloned[0]:   10

After changing clonedTeam[0].level to 99:
Original[0]: 99
Cloned[0]:   99

实际场景:clone() 的适用之处

在 Getter 中进行防御性拷贝

public class Configuration {
    private String[] settings;

    public String[] getSettings() {
        return settings.clone(); // 调用者无法修改内部数组
    }
}

作为深拷贝的构建块

clone() 可以作为更复杂深拷贝流程的第一步,随后手动拷贝数组中可变对象。

最佳实践与常见坑

  • 假设对象数组是浅拷贝。 如果数组中保存的是可变对象,仅使用 clone() 往往不够。
  • 需要独立对象时手动深拷贝。例如对 Player 数组的深拷贝:
Player[] deepCopyTeam = new Player[originalTeam.length];
for (int i = 0; i (original)` or `list.toArray()` for copying.
  • 问:什么时候绝对不该使用 clone()
    答: 当你需要对包含可变对象的数组进行深拷贝,或希望代码意图更明确时;此时 Arrays.copyOfSystem.arraycopy 或手动循环更安全。

  • 问:性能如何?
    答: clone() 是原生的数组拷贝,对浅拷贝来说通常非常快。深拷贝需要额外的工作,速度会随复制的数据量线性下降。

结论:是否应该使用它?

  • 使用 clone():适用于基本类型数组、不可变对象数组(StringInteger 等),或任何浅拷贝已满足需求的场景。
  • 避免 clone():当你需要对可变对象进行独立拷贝时。
  • 考虑替代方案:如 Arrays.copyOfSystem.arraycopy 或显式循环,以获得更清晰的意图和对深拷贝语义的更好控制。

了解 clone() 的工作方式及其局限性,对编写正确且高效的 Java 代码至关重要。

Back to Blog

相关文章

阅读更多 »

为什么要使用设计模式?

重要的是要理解,Design Patterns 从来不是为了被随意拼凑的捷径,也不是以草率的“一刀切”方式应用于……