Java对象克隆与比较方法解析
立即解锁
发布时间: 2025-08-18 00:28:59 阅读量: 15 订阅数: 21 


Effective Java:编写高效Java代码的最佳实践
# Java对象克隆与比较方法解析
## 1. 谨慎重写clone方法
### 1.1 Cloneable接口的问题
Cloneable接口本意是作为混合接口,让类声明自己支持克隆。然而,它存在严重缺陷,其本身没有`clone`方法,而`Object`类的`clone`方法是受保护的。若不借助反射,仅因为一个对象实现了`Cloneable`接口,是无法调用其`clone`方法的,即便使用反射调用也可能失败,因为不能保证对象有可访问的`clone`方法。
### 1.2 Cloneable接口的作用
虽然`Cloneable`接口没有方法,但它决定了`Object`类受保护的`clone`方法的行为:若一个类实现了`Cloneable`接口,`Object`的`clone`方法会返回该对象的逐字段副本;否则,会抛出`CloneNotSupportedException`异常。这是一种非常特殊的接口使用方式,不建议模仿。
### 1.3 clone方法的通用约定
- `x.clone() != x`通常为`true`。
- `x.clone().getClass() == x.getClass()`通常为`true`。
- `x.clone().equals(x)`通常为`true`,但这些都不是绝对要求。
按惯例,`clone`方法返回的对象应通过调用`super.clone`获得。返回的对象应独立于被克隆的对象,可能需要在返回前修改`super.clone`返回对象的一个或多个字段。
### 1.4 不同情况的克隆实现
#### 1.4.1 无可变状态引用的类
若类的所有字段都是基本类型或对不可变对象的引用,调用`super.clone`后可能无需进一步处理。例如`PhoneNumber`类:
```java
// Clone method for class with no references to mutable state
@Override public PhoneNumber clone() {
try {
return (PhoneNumber) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can't happen
}
}
```
#### 1.4.2 有可变状态引用的类
若对象包含对可变对象的引用,简单的克隆实现可能会导致问题。以`Stack`类为例,若克隆方法仅返回`super.clone()`,修改原对象会破坏克隆对象的不变性,反之亦然。正确的做法是递归克隆内部数组:
```java
// Clone method for class with references to mutable state
@Override public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
```
#### 1.4.3 具有复杂可变状态的类
对于像`HashTable`这样内部结构复杂的类,仅递归克隆数组可能不够,还需要复制链表。可以使用递归或迭代的方式来实现:
```java
// Recursive clone method for class with complex mutable state
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// Recursively copy the linked list headed by this Entry
Entry deepCopy() {
return new Entry(key, value,
next == null ? null : next.deepCopy());
}
}
@Override public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[buckets.length];
for (int i = 0; i < buckets.length; i++)
if (buckets[i] != null)
result.buckets[i] = buckets[i].deepCopy();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
... // Remainder omitted
}
```
迭代复制链表的方式:
```java
// Iteratively copy the linked list headed by this Entry
Entry deepCopy() {
Entry result = new Entry(key, value, next);
for (Entry p = result; p.next != null; p = p.next)
p.next = new Entry(p.next.key, p.next.value, p.next.next);
return result;
}
```
### 1.5 克隆复杂可变对象的另一种方法
还可以先调用`super.clone`,将结果对象的所有字段设置为初始状态,然后调用更高级的方法来重新生成原对象的状态。但这种方法与`Cloneable`架构相悖,因为它盲目覆盖了逐字段复制的基础。
### 1.6 设计可继承类时的选择
设计可继承类时,不应实现`Cloneable`接口。可以选择实现一个正常工作的受保护`clone`方法,并声明抛出`CloneNotSupportedException`,让子类自由选择是否实现`Cloneable`;或者提供一个退化的`clone`实现,防止子类实现克隆:
```java
// clone method for extendable class not supporting Cloneable
@Override
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
```
### 1.7 线程安全类的克隆
若编写一个实现`Cloneable`的线程安全类,要确保其`clone`方法正确同步,因为`Object`的`clone`方法未同步。
### 1.8 替代克隆的方法
通常,提供复制构造函数或复制工厂是更好的对象复制方式。复制构造函数接受一个与自身类型相同的参数,复制工厂是静态工厂方法。它们具有诸多优
0
0
复制全文
相关推荐










