《Java 核心技术卷1 第10版》学习笔记------对象克隆【对象拷贝】

 由于克隆并不太常见,而且有关的细节技术性很强,你可能只是想稍做了解,等真正需要时再深人学习。

先来回忆为一个包含对象引用的变量建立副本时会发生什么 。原变量和副本都是同一个对象的引用:

Employee original = new Employee ("John Public", 50000);
Employee copy = original;    // Java 中这叫做引用传递,并非 copy/clone
copy.raiseSalary(10) ; // oops-also changed original

如果希望 copy 是一个新对象, 它的初始状态与 original 相同 , 但是之后它们各自会有自己不同的状态, 这种情况下就可以使用 clone 方法 。

Employee copy = original.clone();    // 克隆,实际上不能直接使用这个方法,详情见下
copy.raiseSalary(10); / / OK original unchanged

不过并没有这么简单 。clone 方法是 Object 的一个 protected 方法 , 这说明你的代码不能直接调用这个方法 【搞不懂为什么的请参看: https://blog.csdn.net/gulang03/article/details/86728054】。 只有 Employee 类可以克隆 Employee 对象 【即 Employee copy = original.clone(); 这种代码只能出现在 Employee 类内部,除非实现  Cloneable 接口,详情往下看】

这个限制是有原因的 。 想想看 Object 类如何实现 clone 。 它对于这个对象一无所知 , 所以只能逐个域地进行拷贝 。 如果对象中的所有数据域都是数值或其他基本类型, 拷贝这些域没有任何问题 、 但是如果对象包含子对象的引用, 拷贝域就会得到相同子对象的另一个引用, 这样一来, 原对象和克隆的对象仍然会共享一些信息【即:默认的克隆操作是 “ 浅拷贝 ” , 并没有克隆对象中引用的其他对象 。】 

浅拷贝会有什么影响吗 ?

这要看具体情况 。 如果原对象和浅克隆对象共享的子对象是不可变的 , 那么这种共享就是安全的 。 如果子对象属于一个不可变的类, 如 String , 就是这种情况 。 或者在对象的生命期中, 子对象一直包含不变的常量 , 没有更改器方法会改变它, 也没有方法会生成它的引用, 这种情况下同样是安全的 。

不过 , 通常子对象都是可变的, 必须重新定义 clone 方法来建立一个深拷贝 , 同时克隆所有子对象 。 在这个例子中, hireDay 域是一个 Date , 这是可变的 , 所以它也需要克隆 。 (出于这个原因 , 这个例子使用 Date 类型的域而不是 LocalDate 来展示克隆过程 。 如果 hireDay是不可变的 LocalDate 类的一个实例, 就无需我们做任何处理了 。)

对于每一个类, 需要确定 :

1 ) 默 认 的 clone 方法是否满足要求 ;
2 ) 是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法 ;
3 ) 是否不该使用 clone。

实际上第 3 个选项是默认选项 。 如果选择第 1 项或第 2 项, 类必须:

1 ) 实现 Cloneable 接口 ;
2 ) 重新定义 clone 方法, 并指定 public 访问修饰符 。【为了 clone() 方法能在任何地方都能使用】

关于 Cloneable 接口的说明:

Cloneable 接口只是一个标记接口(tagging interface),是Java提供的一组标记接口之一。标记接口用途是确保一个类实现一个或一组特定的方法。 标记接口不包含任何方法; 它唯一的作用就是允许在类型查询中使用 instanceof:

if ( obj instanceof Cloneable)
 ...

Cloneable 接口源代码:

public interface Cloneable {
}
// 没有任何待实现的方法

即使 clone 的默认 (浅拷贝 ) 实现能够满足要求 , 还是需要实现 Cloneable 接口 , 将 clone重新定义为 public , 再调用 super . clone() 。

class Employee implements Cloneable{
    public Employee clone() throws CloneNotSupportedException {
        return (Employee) super.clone();
    }
}

与 Object.clone 提供的浅拷贝相比 , 前面看到的 clone 方法并没有为它增加任何功能 。 这里只是让这个方法是公有的 。 要建立深拷贝 , 还需要做更多工作, 克隆对象中可变的实例域 。

下面来看创建深拷贝的 clone 方法的一个例子 :

class Employee implements Cloneable{
    ...

    // 注意访问修饰符改为 public
    public Employee clone() throws CloneNotSupportedException{
        // call Object.clone() 完成不可变字段的拷贝
        Employee cloned = (Employee) super.clone();
        
        // clone mutable fileds 完成可变字段的拷贝
        cloned.hiredDay = (Date) hireDay.clone();
        
        return cloned;
    }
}

必须当心子类的克隆 。 例如, 一旦为 Employee 类定义了 clone 方法, 任何人都可以用它来克隆 Manager【PS:Manager 是 Employee 类的一个子类】对象 。 Employee 克隆方法能完成工作吗 ? 这取决于 Manager 类的域 。 在这里是没有问题的, 因为 bonus 域是基本类型 。 但是 Manager 可能会有需要深拷贝或不可克隆的域 。 不能保证子类的实现者一定会修正 clone 方法让它正常工作 。 出于这个原因, 在 Object 类中 clone 方法声明为 protected 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值