Effective Java笔记:谨慎地覆盖 clone

百度地图街景图像批量获取 由于个人研究需要在最近试了一下使用API获取百度地图街景图片,在此介绍一下基于我个人需求的百度街景批量获取方法,本文以Python编码作为主要方法,如果对零编码工具有需要的可以关注一下UA城市分析小组刚刚发布的街景图片获取工具。 街景图像用作区域细粒度空间指标测度需要解决几个问题。 1. 首先就是调用额度的限制,现在百度对于个人认证开发者每天可用的街景图像调用额度为100张,在不考虑购买额度上限(1万张600元,其实有经费的前提下可以直接考虑钞能力)的情况下动辄几千上万张的爬取数量会大大增加需要爬取的时间周 阅读详情

谨慎地覆盖 clone

在 Java 中,clone 方法及其相关的 Cloneable 接口最初是设计为一种对对象进行拷贝的方法。然而,实际使用中因其存在浅拷贝问题、多态问题、异常处理复杂性等缺陷,clone 方法变得不够直观和安全。因此,开发者通常建议使用 拷贝构造器 或 静态工厂方法 作为替代方案。


1. clone 方法的特点

特性

  1. 浅拷贝 vs 深拷贝

    • super.clone() 默认实现浅拷贝:
      • 基本类型字段会被值拷贝。
      • 引用类型字段仅拷贝引用地址,而不会创建新的对象。
    • 深拷贝需要手动实现,如字段级别的递归克隆。
  2. 受限于 Cloneable 接口

    • Cloneable 是标记接口,只有实现了它,Object.clone() 方法才不会抛出 CloneNotSupportedException。
    • 对于不实现 Cloneable 的类,调用 clone 将抛出异常。
  3. 不会调用构造器

    • clone 是基于内存复制,而不执行构造器逻辑。这可能导致复制出的对象处于未完全初始化或不合法的状态。
  4. 运行时多态问题

    • 如果 clone 方法调用了某个被子类覆盖的方法,可能会导致预期之外的行为。

局限性

(1) 浅拷贝的共享引用

修改克隆对象中的引用类型字段会反作用于原对象,可能导致错误或非预期的共享行为。

@Override
protected Object clone() throws CloneNotSupportedException {
    MyClass cloned = (MyClass) super.clone(); // 浅拷贝
    return cloned;
}

例如,克隆后的对象共享同一个引用:

cloned.someField.modify(); // 修改克隆对象
original.someField.getValue(); // 原对象也受影响
(2) 子类覆盖方法的多态性隐患

在 clone 方法中调用被子类覆盖的方法,可能造成运行时错误。例如:

@Override
protected Object clone() throws CloneNotSupportedException {
    MyClass cloned = (MyClass) super.clone();
    cloned.someMethod(); // 如果子类覆盖了 someMethod,行为可能不可预期
    return cloned;
}
(3) 异常处理复杂性

必须显式处理 CloneNotSupportedException:

try {
    MyClass clone = (MyClass) original.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

2. 谨慎地覆盖 clone 方法

在某些情况下,你可能需要使用 clone 方法。例如,第三方库需要类实现 Cloneable 接口时,或者为了统一项目风格。但是在覆盖 clone 方法时,需要遵循一些最佳实践,以尽量规避其固有的隐患。

谨慎覆盖 clone 方法的步骤

(1) 基本覆盖示例
@Override
protected Object clone() throws CloneNotSupportedException {
    MyClass cloned = (MyClass) super.clone(); // 浅拷贝
    // 添加深拷贝逻辑(如处理引用字段)
    if (this.someField != null) {
        cloned.someField = (SomeClass) this.someField.clone(); // 假设支持 Cloneable
    }
    return cloned;
}
(2) 避免调用可能被覆盖的方法
  • 使用私有或 final 方法封装逻辑,防止子类覆盖时引发问题。
@Override
protected Object clone() throws CloneNotSupportedException {
    MyClass cloned = (MyClass) super.clone();
    cloned.finalizeClone(); // 使用 final 方法
    return cloned;
}

private final void finalizeClone() {
    // 克隆完成后的设置
}
(3) 使用 final 防止子类覆盖
  • 若不希望子类任意修改 clone 方法,可以将其标记为 final。
@Override
protected final Object clone() throws CloneNotSupportedException {
    MyClass cloned = (MyClass) super.clone();
    return cloned;
}

3. 集合类(如散列表)编写 clone 方法

集合类或类似容器的数据结构在 clone 时需要特别小心,因为其内部存储的对象通常是引用类型。

(1) 浅拷贝实现

  • 默认行为仅克隆容器本身,而不会递归克隆容器的内容。
@Override
protected Object clone() throws CloneNotSupportedException {
    HashMap<K, V> cloned = (HashMap<K, V>) super.clone(); // 浅拷贝
    return cloned;
}

(2) 深拷贝实现

  • 如果需要克隆容器中的对象,可以递归实现深拷贝:
@Override
protected Object clone() throws CloneNotSupportedException {
    HashMap<K, V> cloned = (HashMap<K, V>) super.clone();
    cloned.clear();
    for (Entry<K, V> entry : this.entrySet()) {
        K clonedKey = deepCopyKey(entry.getKey());
        V clonedValue = deepCopyValue(entry.getValue());
        cloned.put(clonedKey, clonedValue);
    }
    return cloned;
}

private K deepCopyKey(K key) {
    // 克隆键逻辑
    return key;
}

private V deepCopyValue(V value) {
    // 克隆值逻辑
    return value;
}

4. 替代方案:拷贝构造器和静态工厂方法

4.1 拷贝构造器

定义

拷贝构造器是一种特殊的构造器,用于创建一个新对象,并通过另一个对象来初始化其内容。

优点
  1. 更直观:通过调用 new MyClass(original) 创建新对象。
  2. 无异常问题:避免显式处理 CloneNotSupportedException。
  3. 灵活扩展:可以显式实现深拷贝或自定义初始化逻辑。
实现
public class MyClass {
    private String name;
    private SomeClass someField;

    // 拷贝构造器
    public MyClass(MyClass original) {
        this.name = original.name;  // 浅拷贝
        this.someField = new SomeClass(original.someField); // 深拷贝
    }
}
使用
MyClass original = new MyClass(...);
MyClass copy = new MyClass(original); // 使用拷贝构造器

4.2 静态工厂方法

定义
  • 静态工厂方法是一个 static 方法,用来提供类的自定义实例化逻辑。
  • 是拷贝构造器的变形版本,适合更灵活的场景。
优点
  1. 灵活性高:可以根据需要返回父类或子类的实例。
  2. 容易管理复杂的初始化逻辑。
实现
public class MyClass {
    private String name;
    private SomeClass someField;

    public MyClass(String name, SomeClass someField) {
        this.name = name;
        this.someField = someField;
    }

    // 静态工厂方法
    public static MyClass copy(MyClass original) {
        return new MyClass(original.name, 
                           new SomeClass(original.someField)); // 深拷贝
    }
}
使用
MyClass original = new MyClass(...);
MyClass copy = MyClass.copy(original); // 使用静态工厂方法

5. 总结与对比

特性clone 方法拷贝构造器静态工厂方法
实现复杂性使用 Cloneable 接口,稍显复杂简单直接更灵活适合复杂场景
异常处理必须处理 CloneNotSupportedException无需异常处理无需异常处理
深拷贝灵活性手动实现,易出错易于实现深拷贝易于实现深拷贝
继承与扩展容易受到子类覆盖方法影响子类需显式实现父类 clone 行为可返回父类或子类对象
使用安全性运行时多态可能引发问题显式控制更安全显式控制更安全
性能和复杂性性能好,但容易误用较好,易于维护性能好,适合复杂场景

最佳实践

  1. 如果有严格需求(如继承体系中需要统一克隆),可以使用 clone 方法,但要遵循谨慎覆盖的原则。
  2. 更推荐 拷贝构造器 和 静态工厂方法,它们更灵活、直观、安全,尤其适合深拷贝和复杂场景。
  3. 根据项目需求权衡深拷贝与浅拷贝,合理设计对象的拷贝逻辑,避免共享引用问题。
Cartographer参数调优指南:如何用Lua配置解决建图漂移问题 本文深入解析Cartographer SLAM系统的Lua参数调优方法,针对建图漂移问题提供完整解决方案。从运动滤波、扫描匹配到多传感器融合,详细讲解关键参数配置技巧,并给出仓库物流和服务机器人等典型场景的优化方案,帮助开发者实现精准建图。 阅读详情

相关推荐

Visio 2013安装遇阻?手把手教你搞定注册表权限错误1402(附64位系统特殊处理)

本文详细解析了Visio 2013安装过程中常见的注册表权限错误1402问题,特别是在64位系统下的特殊处理方法。通过分步指导如何修改注册表权限、处理Wow6432Node分支以及使用高级工具如subinacl,帮助用户顺利完成安装并避免常见连带问题。

t8u9v0w1x的博客 544

effective java读书笔记11:谨慎地覆盖clone

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34307464的博客 103

保姆级教程:在寒武纪MLU370上从零部署YOLOv5(附安全帽数据集)

本文提供在寒武纪MLU370上从零部署YOLOv5的保姆级教程,涵盖环境配置、模型迁移、安全帽数据集处理、训练优化及推理部署全流程。通过实战案例展示MLU370在目标检测任务中的高效性能,适合开发者快速掌握国产AI加速卡的应用技巧。

weixin_27419879的博客 189

《Effective Java》第13条:谨慎的覆盖clone

原文P51:克隆复杂对象的最后一种办法是,先调用super.clone方法,然后把结果对象中的所有域都设置成它们的初始状态,然后调用高层的方法来重新产生对象的状态。原文P47:如果类的clone方法返回的实例不是通过调用super.clone方法获得,而是通过调用构造器获得,编译器就不会发出警告,但是该类的子类调用了super.clone方法,得到的对象就会拥有错误的类,并阻止了clone方法的子类正常工作。因此,上一段中讨论到的put(key,value)方法要么应是final的,要么应是私有的。

TD_FAlmK的博客 698

主流仿真软件Sigrity/HFSS/Siwave/Hyperlynx/ADS差异

在电子设计自动化(EDA)领域,各种仿真软件如Cadence Sigrity、Mentor Graphics的HyperLynx、Keysight的ADS以及ANSYS的SIwave等,都广泛应用于不同的应用场景中。综上所述,这些EDA仿真软件在各自的应用场景中发挥着重要作用,帮助工程师在设计阶段就能够评估和优化电子产品的性能,提高设计的可靠性和效率。:Sigrity由全球最大的EDA公司Cadence推出,Cadence还推出了Orcad和Allegro等著名软件。:由安捷伦(现是德科技)公司开发。

Geektec的专栏 9403

EDKII环境配置完整版

参考博客【EDK2 UDK2018的环境搭建】:https://blog.youkuaiyun.com/qq_39155263/article/details/82965744下载EDK2 1.直接从https://github.com/tianocore/edk2/ 网站下载.zip压缩文件 2.运用svn 从http://svn.code.sf.net/p/edk2/code/trunk/edk2edk2下载 3.安装Visual Studio2017 安装其他Tool 1.IASL 从https..

tianpu2320959696的博客 1854

《Effective Java》阅读笔记 11谨慎地覆盖clone

1.序 Object 中 clone 方法的定义是: protected native Object clone() throws CloneNotSupportedException; 调用clone()方法需要对象实现Cloneable接口,该接口决定了Object中受保护的clone方法的实现行为: 如果一个类实现了Cloneable接口,Object的clone方法返回该对象的逐域拷贝; 如果一个类未实现Cloneable接口,则该对象就会抛出CloneNotSupportedException

weixin_41563161的博客 332

苏宁易购手机行情比价系统:构建一个完整的Python爬虫实战指南

在当今数字化时代,智能手机已成为人们生活中不可或缺的一部分。随着各大手机品牌每年推出多款新品,消费者面临着越来越多的选择。然而,不同电商平台、不同时间段的价格波动使得消费者很难把握最佳购买时机。苏宁易购作为国内领先的电商平台之一,拥有丰富的手机商品信息和价格数据。本文将带领读者构建一个完整的苏宁易购手机行情比价系统,帮助消费者实时掌握手机价格动态。

2201_76125261的博客 207

跟我学(Effective Java 2)第11条:谨慎地覆盖clone方法

第11条:谨慎地覆盖clone方法 Cloneable接口表明这样的对象时允许克隆的,但这个接口并没有成功达到这个目的,主要是因为它缺少一个clone方法,Object的clone方法是受保护的。如果不借助反射,就不能仅仅因为一个对象实现了Colneable就可以钓鱼clone方法,即使是反射调用也不能保证这个对象一定具有可访问clone方法。 既然Cloneable并没有包含任何方法,那么它到底...

Dullon_jiang的博客 252

11、机器学习中的聚类与隐马尔可夫模型

本文介绍了机器学习中的两种重要方法:聚类算法和隐马尔可夫模型(HMM)。聚类作为无监督学习的代表,适用于发现无标签数据中的结构,常见算法包括K-均值和自组织映射(SOM),并在传感器数据分析等场景中展现应用价值。隐马尔可夫模型则是一种可解释的序列模型,用于从观测数据中推断隐藏状态,广泛应用于语音识别、自然语言处理和生物信息学等领域。文章详细讲解了HMM的核心概念如马尔可夫性质、转移与发射概率,并通过TensorFlow实现了前向算法,展示了其计算观测序列概率的动态规划思想。最后对比了两类方法的应用场景,展望

garlic的博客 179

8. Spring Ai之入门到精通(超级详细)

Spring AI 1.0.0 M1里程碑版本发布,标志着 Java 终于踏进Ai的时代半只脚。

gorgor在码农 3万+

Effective Java – Item 13 谨慎地覆盖 clone

item13 · 谨慎地覆盖 clone Cloneable 接口的目的是作为对象的一个 mixin 接口,表明这样的对象允许克隆。但,它并没有成功地达到这个目的,它的主要缺陷在于缺少一个 clone 方法,而 Object 的 clone 方法是受保护的。 Cloneable 作用 决定了 Object 中受保护的 clone 方法实现的行为 如果一个类实现了 Cloneable 接口,O...

AlbumenJ的博客 219

《FPGA DDR3权威实战:从基础到高速故障录波系统》专栏导览:从不知所措到心中有数

本文是一份DDR3实战指南,作者从自身学习经历出发,分享了一套循序渐进的DDR3应用路线图。专栏包含12篇文章,从基础配置(MIG IP核、AXI总线)、仿真测试到实际项目应用(功能测试、性能测试),最后深入原生MIG接口实战,通过一个高速故障录播系统案例完整展示DDR3开发流程。文章特别强调实践性,旨在帮助读者系统掌握DDR3开发技能,避免作者当年踩过的坑。每篇都配有优快云链接,适合FPGA开发者按顺序学习参考。

FPGA_小田老师的博客 331

Effective Java 谨慎的覆盖clone()方法

一个类要想实现克隆,需要实现Cloneable接口,表明这个类的对象具有克隆的功能。 Cloneable接口是一个mixin接口,它里面并没有任何的抽象方法,类似的接口有Serializable接口,表明该类的对象可以序列化。 首先应该明确通过一个对象克隆出另一个对象的概念:通过一个对象克隆出另一个对象,简称对象1和对象2,这两个对象就成为两个独立的对象, 那么对对象1做任何的修改,对象2应该不受影响;对对象2做任何的修改的,对象1就不应该受影响。这样就要求对象1与对象2的实例域各自是独立的。 那么我们知道

菜园子丶For丨丶Tomorrow的博客 207

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Effective Java笔记(13)谨慎地覆盖 clone

Cloneable 接口的目的是作为对象 的 一个 mixin 接口( mixin interface ) ,表明这样的对象允克隆( clone ) 。 遗憾的是,它并没有成功地达到这个目的 。 它的主要缺陷在于缺少一个 clone 方法, 而 Object 的 clone 方法是受保护的 。 如果不借助于反射( reflection) ,就不能仅仅因为一个对象实现了 Cloneable ,就调用 clone方法 。 即使是反射调用也可能会失 败 ,因为不能保证该对象一定具有可访问的 clone 方法 。

琅琊之榜 322

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

EffectiveJava学习笔记7:始终要覆盖toString、谨慎覆盖clone

1.始终要覆盖toString 个人理解:toString就是当把某一对象作为字符串输出时会调用这个方法。(比如:printf(xx); "a"+xx;),其实际上都可看成为printf(xx.toString()); "a"+xx.toString(); Object虽然有提供toString,但其输出格式是以下形式的(类名@散列码的无符号16进制) public String to...

沙丁鱼的博客 246

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

《Effective Java》解读第13条:谨慎地覆盖clone

摘要:Java的clone方法存在诸多设计缺陷,不推荐直接使用。主要问题包括:语义不明确(深/浅拷贝)、绕过构造器、Cloneable接口异常机制不合理、与final域冲突等。若必须实现clone,应遵循规范:实现Cloneable接口,调用super.clone()进行浅拷贝,并递归处理可变对象字段。更推荐的替代方案是使用拷贝构造器或静态工厂方法,它们更灵活安全,能避免clone的固有缺陷,尤其适合处理含引用类型字段的对象复制需求。

Cosmoshhhyyy的博客 481

Effective Java 【对于所有对象都通用的方法】第13条 谨慎地覆盖clone

目录谨慎地覆盖cloneCloneable接口并没有包含任何方法,那么它到底有什么作用呢?Object类中的clone()方法如何重写好一个clone()方法1.对于数组类型我可以采用clone()方法的递归2.如果对象是非数组,建议提供拷贝构造器(copy constructor)或者拷贝工厂(copy factory)3.如果为线程安全的类重写clone()方法4.如果为需要被继承的类重写clone()方法总结 谨慎地覆盖clone Cloneable接口地目的是作为对象的一个mixin接口(详见第20

zhangxiaoxiao9527的博客 377

《Effective Java》第11条: 谨慎地覆盖clone

一. 什么情况下能够用到clone() 当我们需要一个与原对象一模一样的副本时,并且对副本进行改动而不会影响到原对象时,需要使用到clone(). 二.深拷贝与浅拷贝 浅拷贝:浅拷贝只是单纯的对于对象的拷贝,对象属性对于其它对象的引用并没有进行拷贝(也就是说浅拷贝创建的对象和原来对象指向不同的地址空间,但是对象属性里面对其它对象的引用【引用属性】指向的还是同一...

qq_34332035的博客 406

《Effective Java》谨慎的覆盖clone()方法

首先说一些,clone()是什么?应该怎么用? clone()方法是Object类中的方法,protected修饰符修饰,用来复制对象。 下面是结果: 可以看到clone()创建了一个完全不同的对象,t2指向了一个新的在堆中的对象。使用clone()方法需要实现Cloneable接口,如果不实现的话会抛出CloneNotSupportedException,这个接口并没有什么实际意义,只...

Joseph_Geeker的博客 479

Effective Java - 对于所有对象都通用的方法 - 谨慎地覆盖 clone

读书笔记 仅供参考Cloneable 接口Cloneable 借口表明这样的对象允许克隆,但是它缺少 clone 方法。 Object 拥有一个 受保护的 clone 方法,如果不采用反射,无法调用。 Cloneable 中并没有方法,它只是决定了 Object.clone() 的实现行为:如果一个类实现了 Cloneable,clone 方法就会返回对象的逐级拷贝,否则抛出 CloneNotS

wujunyucg的博客 303

笔记《Effective Java》02:对所有对象都通用的方法(文末抽奖)

然而,它没有达到这个目的,主要的缺陷是缺乏 clone 方法,而 Object 类中的 clone 方法是受保护的。相反,请使用基本类型的封装类中的静态compare方法,或使用Comparator接口中的比较器构造方法。他们不需要类型转换。Java8中,Comparator接口提供了一组比较构造器方法,这些比较器可以用来实现Comparable接口所要求的compareTo方法,不过性能上会稍微慢一些。虽然Object类提供了toString方法的一个实现,但他返回的字符串通常不是类的用户所希望看到的。

有一只柴犬的专栏 1301

Effective Java 对于所用对象都通用的方法 11.谨慎地覆盖clone

Cloneable 接口的目的是作为对象的一个mixin接口,表明这样的对象允许克隆。但是他缺少clone方法,object的clone方法是受保护的。如果不借助于反射,不能因为一个对象实现了Cloneable接口,就可以调用clone方法。即使反射调用也可能失败,因为不能保证该对象一定具有可访问的clone方法。那么如何实现一个行为良好的clone方法?如果一个类实现了cloneable,obje

yancyang的博客 2万+

《Effective Java》读书笔记09--谨慎地覆盖clone方法

一、Cloneable接口与Object.clone方法 Cloneable接口的目的是作为对象的一个mixin接口(混合型接口),表明这样的对象允许克隆(clone)。遗憾的是Cloneable接口里并没有clone方法,其实它什么方法都没有,跟Serializable接口一样,都是占着茅坑不拉屎。它只是声明该对象可以被克隆,具体行为由类设计者决定。如果类设计者忘记提供一个良好的clone方法

Java Dynamic的专栏 1960

快速带你看完《Effective Java》—— 对象通用方法篇

文章目录10 覆盖equals时要遵守通用约定11 覆盖equals时总要覆盖hashCode12 始终要覆盖toString13 谨慎地覆盖clone14 考虑实现Comparable接口 豆瓣评分9.8的图书《Effective Java》,是当今世界顶尖高手Josh Bloch的著作,在我之前的文章里我也提到过,编程就像练武,既需要外在的武功招式(编程语言、工具、中间件等等),也需要修炼心法(设计模式、源码等等)学霸、学神OR开挂 我个人在Java领域也已经学习了近5年,在修炼“内功”的方面也通过各

如切如磋,如琢如磨 1225

【Effective Java】Ch3_Methods:Item11_谨慎重写clone()

Cloneable接口的目的是作为对象的一个mixin接口,表明对象允许克隆;但这个目的没有达到。 其主要缺点是,Cloneable缺少一个clone()方法,而Object.clone()是受保护的。 通常,实现接口是为了表明类可以为它的客户做些什么;而Cloneable却是改变了超类中受保护方法的行为。 Object.clone()定义的约定: /** 创建并返

Alpha's 学习笔记 1509
上一篇: Effective Java笔记 :覆盖 equals 时请遵守通用约定
下一篇: Effective Java笔记:考虑实现 Comparable 接口
Jooou
博客等级 码龄9年 226粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值