Effective Java笔记:要么设计继承并提供文档说明,要么禁止继承

Outlook日历同步全攻略:Win10+安卓+iOS三端无缝衔接(附常见问题解决) 本文提供了一份详尽的Outlook日历同步指南,帮助用户在Windows 10、安卓和iOS三端实现无缝衔接。文章深入解析了Exchange ActiveSync等核心同步原理,并给出了从账户创建到各设备(特别是华为手机)的具体配置步骤、进阶管理技巧以及针对同步失败、事件重复等常见问题的实战解决方案,旨在彻底解决跨平台日程管理的痛点。 阅读详情

这个观点强调的是在使用继承时,设计者必须明确其意图,并为继承行为提供清晰的文档说明来指导子类的扩展。如果无法提供合适的设计和说明,那么禁止继承反而是更好的选择。这项原则通常用于设计复杂或者面向公共 API 的系统,比如开源框架和库的开发中。


核心观点

  1. 继承是一种强大的工具,但也非常危险:

    • 继承为子类提供了复用父类代码的能力,同时允许子类重写和扩展的方法。
    • 但它也意味着子类和父类之间形成了紧密的依赖关系。父类的任何实现细节,都可能直接或间接影响子类的行为。
  2. 良好的继承设计需具备文档指导:

    • 子类如何扩展父类?什么行为可以被重写?哪些方法只能被调用而不能重写?这些都需要在设计时明确写入文档。
    • 未考虑到子类的行为可能导致继承滥用,让子类破坏父类代码逻辑,或者使子类难以维护(例如意外依赖隐含的逻辑)。
  3. 继承不是默认选项,禁止继承也是一种选择:

    • 如果父类的设计无法清晰地说明继承的用途;或者继承的潜在问题(API 被子类重写、范式被破坏等)大于带来的好处,那么设计者应通过技术手段(如将类声明为 final 或者使用组合)禁止继承。

两种策略的实现

我们可以遵循以下两种策略来应用这条原则:“要么设计继承并提供文档说明,要么禁止继承”。


1. 设计继承并提供清晰的文档说明

1.1 明确继承的目标

在设计一个允许继承的父类时,需要明确回答以下问题:

  • 子类为什么需要继承?
  • 哪些方法允许子类重写?
  • 子类继承后是否可以修改行为?如果可以,什么是允许的修改范围?
  • 子类需要遵循哪些契约(即继承是否有规范)?

示例 1:定义一个允许继承的类

/**
 * 用于提供幂操作的一些方法。
 * 本类可以被继承,用于对具体的乘幂算法进行扩展。
 * 
 * 注意:
 * - 子类必须保证 `power` 方法的输入始终大于或等于 0。
 * - 子类可以重写 `calculatePower` 方法来实现自定义的幂运算。
 */
public class PowerCalculator {

    /**
     * 计算 base^exponent 的幂
     * @param base 底数
     * @param exponent 指数,必须大于等于 0
     * @return 幂运算结果
     */
    public final double power(double base, int exponent) {
        if (exponent < 0) {
            throw new IllegalArgumentException("Exponent must be >= 0.");
        }
        return calculatePower(base, exponent);
    }

    /**
     * 子类可以覆盖此方法以实现自定义的幂运算逻辑。
     * 请确保此方法在 `power` 方法的前提条件范围内运行。
     */
    protected double calculatePower(double base, int exponent) {
        return Math.pow(base, exponent); // 默认实现使用 Java 内置的 pow 方法
    }
}

关键点:

  1. 使用 Javadoc 来明确说明允许继承的意义及其使用限制。
  2. 通过 final 修饰 power 方法,防止子类直接重写核心逻辑。
  3. 提供一个 protected 的 calculatePower 方法作为扩展点,允许子类实现自定义逻辑。

子类场景:

public class ApproximatePowerCalculator extends PowerCalculator {
    @Override
    protected double calculatePower(double base, int exponent) {
        // 简单估算的方法
        return base * exponent; // 示例:简单返回 base * exponent(错误的实现)
    }
}

1.2 限制继承范围

当一个类的实现很容易因子类滥用而出问题时,应尽可能明确哪些部分是封闭的(不可重写),哪些部分开放给子类(可重写)。

  • 将类的关键逻辑封闭(使用 final 或 private)。
  • 提供专门针对扩展的“钩子方法”。

示例 2:模板方法模式(Template Method Pattern)

public abstract class AbstractModel {

    /**
     * 模板方法,规定了算法的骨架,子类不能修改。
     * 始终调用特定步骤(子类通过钩子扩展它们)。
     */
    public final void execute() {
        step1();
        step2();
        step3();
    }

    protected void step1() {
        System.out.println("Step 1: Default implementation");
    }

    protected abstract void step2(); // 由子类实现

    private void step3() {
        System.out.println("Step 3: This is a private step.");
    }
}

扩展方式:

public class CustomModel extends AbstractModel {
    @Override
    protected void step2() {
        System.out.println("Step 2: My custom implementation");
    }
}

public class Main {
    public static void main(String[] args) {
        AbstractModel model = new CustomModel();
        model.execute();
        // 输出:
        // Step 1: Default implementation
        // Step 2: My custom implementation
        // Step 3: This is a private step.
    }
}

通过模板对算法的步骤进行固定,确保子类只能修改被允许的部分,例如 step2,而其他部分对子类是封闭的。


2. 禁止继承

当一个类不适合被子类化时,可以通过以下方式禁止继承:

2.1 使用 final 关键字

通过将一个类声明为 final,可以禁止任何类从这个类继承。

public final class UtilityClass {
    public static int add(int a, int b) {
        return a + b;
    }
}

对于工具类(跟逻辑无关,只保存一些静态方法),禁止继承是一种推荐的设计方法。


2.2 将构造函数声明为 private

通过使构造函数 private 并公开一个静态工厂方法,可以完全限制从类外实例化或继承。

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        // 私有的构造函数
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

这种设计确保了 Singleton 类完全不能被继承,因为外界无法访问它的构造函数。


2.3 使用复合代替继承

在很多情况下,复合是一种可以代替继承的更安全的设计模式(见之前“复合优于继承”的讨论)。通过组合其他类而非继承,可以提高类的可维护性和封装性。

class Car {
    private Engine engine; // 使用复合
    // ... methods
}

总结

  1. 要么设计继承并提供文档说明:

    • 允许继承时,需明确设计继承的目标,约束继承的行为(通过 final 或模板方法),并通过文档详细说明子类扩展的重要信息和规则。
  2. 要么禁止继承:

    • 如果一个类不需要继承,或者继承可能导致误用(如工具类、不可变类、单例等),优先禁止它(使用 final 或复合设计)。

这条原则的本质是减少类之间的耦合性,同时增强代码易读性和可维护性。根据需求与设计目标选择允许继承或组合机制。

2023年五一数学建模 | 第二十届五一数学建模C题:“双碳”目标下低碳建筑研究 阅读详情

相关推荐

Homebrew 必备软件清单:我的 macOS 开发环境搭建全记录

本文详细介绍了如何使用Homebrew在macOS上搭建高效的开发环境,包括基础配置、核心开发工具链、高效编码工具集等30个必备工具。通过Homebrew这一macOS缺失的包管理器,开发者可以一键安装命令行工具和GUI应用,大幅提升工作效率。文章还提供了日常维护命令和疑难问题解决方案,适合从新手到资深开发者的所有用户。

weixin_28836507的博客 353

Effective Java笔记(19)要么设计继承并提供文档说明,要么禁止继承

专门为了继承而设计类是一件很辛苦的工作。你必须建立文档说明其所有的自用模式,并且一旦建立了文档,在这个类的整个生命周期中都必须遵守。如果没有做到,子类就会依赖超类的实现细节,如果超类的实现发生了变化,它就有可能遭到破坏。为了允许其他人能编写出高效的子类,还你必须导出一个或者多个受保护的方法。除非知道真正需要子类,否则最好通过将类声明为final,或者确保没有可访问的构造器来禁止类被继承。

琅琊之榜 313

嵌入式开发避坑指南:用DWT做延时函数,这几个细节不注意代码会跑飞

本文深入探讨了在Cortex-M嵌入式开发中使用DWT实现精确延时函数的关键技巧与常见陷阱。针对DWT_CYCCNT计数器应用中的内核兼容性、溢出处理、编译器优化等五大核心问题,提供了工业级稳健解决方案,帮助开发者避免代码跑飞风险,实现微秒级高精度延时控制。

weixin_42653691的博客 160

Effective Java笔记第三章类和接口第五节要么为继承而设计,并提供文档说明,要么就禁止继承

Effective Java笔记第三章类和接口 第五节要么为继承而设计,并提供文档说明,要么就禁止继承 1.对于专门为了继承而设计并具有良好文档说明的类而言,首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。换句话说,该类必须有文档说明它可覆盖的方法的自用性。对于每个公有的或受保护的方法或者构造器,他的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续的处理过程的(所谓可覆盖的方法,是指非final的,公有的或受保护的)。更一般地,类必须在文档中说明

zh123456_789的博客 216

记一次网络风暴/网络环路

环境: win10 x64 ,联想云教室,电子教室,华三交换机。 状况描述: 最开始开机,一直卡在PXE启动界面,进不去系统。电脑技术张工说,先开教师机后开学生机。因为学生机开机需要读取教师机的镜像。但是状况依旧。 开始排查网络,重启交换机后,发现陆续有几台电脑能登陆进系统桌面了,但很慢起码过了5分钟以上才能进去。 教师机一直都是正常的,然后我在教师机部署电子教室。技术说这电子 教室与云教室IP冲突。 (那之后我查了下这两款软件的PID和端口,所占端口不一样,那没冲突) 我说不可能吧,这套软件在

Nightwish5的博客 2865

Jetson平台XDMA PCIe驱动安装与测试全指南

本文提供了一份在NVIDIA Jetson平台上安装与测试Xilinx FPGA XDMA PCIe驱动的完整指南。详细阐述了驱动作为高速DMA引擎的核心作用,并逐步讲解了从环境准备、驱动编译、内核模块加载到实际数据传输测试的全过程。内容涵盖常见问题排查与性能优化建议,帮助开发者实现Jetson与FPGA间的高速数据通信,适用于自动驾驶、工业视觉等需要异构计算的应用场景。

weixin_29196315的博客 526

《Effective Java》第19条:要么设计继承并提供文档说明,要么禁止继承

对于每个公有的或受保护的方法或者构造器,它的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续处理过程的(所谓可覆盖的方法,是指非final的、公有的或受保护的)。更广义地说,即类必须在文档中说明,在哪些情况下它会调用可覆盖的方法。,因为父类构造器的执行顺序优先于子类构造器,如果父类构造器调用了可被覆盖的方法,那么就有可能在子类构造器还未执行的情况下,执行了子类的重写方法,如果该重写的方法依赖于子类构造器所执行的任何初始化工作,该方法将不会如预期般执行。

TD_FAlmK的博客 1054

低成本实现webhook接收端[python]

Webook本质上也是API,只不过是反向调用,即前端不主动发送请求,完全由后端推送。简单来说,Webhook是一个接受HTTP POST或是GET,PUT,DELETE的URL,一个实现了Webhook的API提供商就是在当事件发生的时候会向配置的URL发送信息,与请求-响应式不同,使用Webhook可以实时接收到变化。Ps:Webhook可能会发出大量的请求,确保接受的URL不被泄露或者应用能及时处理这些请求。

锡城小凯的博客 1万+

Effective Java 类和接口 第17条:要么为继承而设计,并提供文档说明,要么就禁止继承

第16条提醒我们,对于不是为了继承而设计,并且没有文档说明的“外来”类进行子类化是多么危险。那么对于专门为了继承而设计并具有良好文档说明的类而言,这有意味着什么呢? 该类的文档必须精确的描述覆盖每个方法所带来的影响。(java.util.AbstractCollection) /** * Removes a single instance of the specified el...

yancyang的博客 1万+

解决PADs VX2.7安装中的License失效与软件卡死问题

本文详细解析了PADs VX2.7安装过程中常见的License失效与软件卡死问题,提供了从破解工具选择、环境变量设置到输入法兼容性调整等实用解决方案。特别推荐使用Siemens EDA系列新版破解工具,并分享性能调优与定期维护技巧,帮助工程师高效解决安装难题。

weixin_29234239的博客 813

DDR5中的On-Die Termination(ODT)详解:电阻值支持及其背后的秘密

DDR5支持多档ODT电阻值,范围从40Ω到480Ω,均基于240Ω标准参考电阻RZQR_{ZQ}RZQ​。不同电阻档位对应不同的信号负载和功耗需求。校准电压点关键,常用0.8×VDD进行精准调校。CA信号间ODT匹配必须严格控制,保证命令总线稳定。选择合适的ODT值是高速内存设计中不可忽视的细节。

weixin_43199439的博客 2099

《Effective Java》阅读笔记 17要么为继承而设计,并提供文档,要么就禁止继承

面向对象编程,从一开始被洗脑难免在上手写代码时都会首先思考有没有公共方法啊,能不能把两个类抽象成一个父类再继承啊等,慎重使用继承,当要使用继承时一定要在文档注释中写明重写这个方法会给其他方法带来什么影响。书中给出建议如果类并不是为了继承而生,那么这个类应该用final修饰禁止子类化。 1.继承的缺陷 对于为了继承而设计的类,唯一的测试方法就是编写子类。 为了继承,类还必须遵守其他一些约束。构造器绝不能调用可被覆盖的方法,无论是直接调用还是间接调用。 无论是clone还是readObject,都不可以调用可

weixin_41563161的博客 271

基于STM32的智能血氧、心率、体温、血压监测系统

摘要:本文设计了一款基于STM32F103C8T6的多功能健康监测系统,可实时检测血氧、心率、体温和血压四项生理参数。系统集成MAX30102、DS18B20等传感器模块,采用OLED显示和蓝牙无线传输技术,实现一键血压测量、阈值报警及手机APP数据管理功能。硬件设计包含传感器采集、电源管理、显示报警等模块;软件采用模块化架构,实现数据采集处理、蓝牙通讯等功能。测试表明系统测量准确可靠,为家庭健康监测提供了便携解决方案。未来可扩展更多监测参数和智能分析功能。

3254968421的博客 771

Effective Java 要么为继承设计,并提供文档说明,要么就禁止继承

首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。换句话说,该类必须有文档说明他可覆盖(overridable)的方法的自用型(self-use)。 对于每个共有的或受保护的方法或构造器,他的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续的处理过程的。更一般的,类必须在文档中说明,在哪些情况下会调用可覆盖的方法。(例如:后台的线程或者静态的初始化可能会调用这个方法) 按惯例,如果方法调用了可覆盖的方法,在他的文档注释的末尾应该包含关于这些调用的描

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

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

要么设计继承并提供文档说明,要么禁止继承——《effective Java》 第三版

为了继承而进行的设计不仅仅涉及自用模式的文档设计。为了使程序员能够编写出更加有效的子类,而无须承受不必要的痛苦,类必须以精心挑选的受保护的(protected)方法的形式,提供适当的钩子(hook),以便进入其内部工作中。这种形式也可以是罕见的实例,或者受保护的域。例如,以java.util.AbstractList 中的 removeRange 方法为例: protected void removeRange(int fromIndex, int toIndex) (从列...

m0_65546127的博客 681

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

跟我学(Effective Java 2)第17条:要么为继承而设计,并提供文档说明,要么就禁止继承

第17条:要么为继承而设计,并提供文档说明,要么就禁止继承 首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。换句话说,该类必须有文档说明他可覆盖(overridable)的方法的自用型(self-use)。对于每个共有的或受保护的方法或构造器,他的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续的处理过程的。更一般的,类必须在文档中说明...

Dullon_jiang的博客 277

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

《Effective Java》解读第19条:要么设计继承并提供文档说明,要么禁止继承

摘要: 继承是强大但危险的机制,除非类专门为继承设计,否则应禁止继承。为继承设计的类需详细文档说明可重写方法的影响,提供protected方法供安全重写,并避免在构造器、clone和readObject中调用可重写方法。禁止继承的两种方式:声明类为final,或将构造器设为private/package-private并通过静态工厂提供实例。由于为继承设计需要大量工作且限制多,大多数普通类应禁止继承以保证安全。(149字)

Cosmoshhhyyy的博客 251

Effective Java 第17条:要么为了继承而设计,并提供文档说明,要么禁止继承

其实在 16 条中说过继承的缺点:  可能会导致子类很脆弱,如果超类用了自用模式并且没有提供相关的文档说明。  后期版本更新中,有可能导致子类实现的方法和超类中同名,从而成为覆盖父类方法。 所以设计超类的时候需要注意这些实质性的规则: 首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。该类必须有文档说明其可改写的方法的自用性:对于每一个公有的或

Come on ! Just Do it ! 2967

Effective Java 学习笔记--第18、19条继承与复合

介绍继承与复合的设计以及两者的优劣分析

weixin_50353846的博客 1088

《Effective java》读书记录-第17条-要么为继承而设计,并提供文档说明,要么就禁止继承

可继承类必须有文档说明它可覆盖(overridable)的方法的自用性(self-use)。 好的API文档应该描述一个给定的方法做了什么工作,而不是描述它是如何做到的。 类必须通过某种形式提供适当的钩子(hook),以便能够进入到它的内部工作流程中,这种形式可以是精心选择的受保护(protected)方法,也可以受保护的域(比较少见)。 对于为了继承而设计的类,唯一的测试方法就是编写子类。

sunyx1130的专栏 693

effective java(17) 之要么为继承而设计,并提供文档说明,要么就禁止继承

effective java 之要么为继承而设计,并提供文档说明,要么就禁止继承 1、 对于专门为了继承而设计并且具有良好文档说明的类而言,该类的文档必须精确地描述覆盖每个方法所带来的影响。 该类必须有文档说明它可覆盖的方法的自用性。 对于每个公有的或受保护的方法或者构造器,它的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续

Eric的博客 447

学习effective java-17类和接口之要么为继承而设计,并提供文档说明,要么就禁止继承

该知识点是自己从书籍中学习的笔记。 第16条告诉了我们对一个不是继承而设计并且没有文档的类是很危险的。那么一个为了设计而使用继承并且有很好的文档则应该做以下事情: 该类的文档必须精确地说明每个改写的方法作用以及影响。好的API文档:应该是描述该方法做了什么工作,而不是如何做的。 一个为了继承而设计的类(超类)应该是: a.为了允许被继承,无论是直接还是间接,构造方法都不能够调用非final

啦啦的专栏 775

Effective Java - 类和接口 - 要么为继承而设计,并提供文档说明,要么就禁止继承

读书笔记 仅供参考文档需要说明该方法调用了哪些可覆盖的方法,以什么顺序,调用结果有什么影响,在哪些情况会调用可覆盖的方法 决定应该暴露哪些受保护的方法或域是很难的。 对于为了继承而设计的类,唯一的测试方法就是编写子类(3个就可以测试) 构造器不能调用可被覆盖的方法

wujunyucg的博客 377

Effective Java读书笔记-6

Effective Java读书笔记-6使可变性最小化复合优先于继承要么设计继承并提供文档说明,要么禁止继承 使可变性最小化 不可变类是指其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期(lifetime)内固定不变。不可变的类比可变类更加易于设计、实现和使用。它们不容易出错,且更加安全。为了使类成为不可变,要遵循下面五条规则: 1.不要提供任何会修改对象状态的方法(也称为设值方法)。 2.保证类不会被扩展。 为了防止子类化,一般做法是声明这个类成为fin

weixin_42583701的博客 227

Effective Java 第三版 中文翻译

effective-java-third-edition 介绍 Effective Java 第三版全文翻译,纯属个人业余翻译,不合理的地方,望指正,感激不尽! 目录 推荐序 前言 致谢 第一章 引言 第二章 创建和销毁对象   本章涉及创建和销毁对象,包括何时以及如何创建它们,何时以及如何避免创建它们,如何确保它们被及时销毁,以及如何管理在销毁之前必须进行的清理操作。 第1项:考虑静态工...

Coloured_Glaze的博客 3861

Effective Java读书笔记

第一章 引言 本文用来记录Effective Java学习过程中的各个知识点,受此时能力所限,有些点可能讲的不太清楚,或者理解有错误的地方,请多多指教! 第二章 创建和销毁对象 第一条 用静态工厂方法代替构造器 可参考文章:https://blog.youkuaiyun.com/u014129886/article/details/89670049 总而言之,静态工厂方法和共有构造器各有各的用处,我们需要理解他们各自的长处,静态工厂经常更加合适。 此处的静态工厂方法与设计模式中的工厂模式不同,这里是一种构造

逆水行舟,不进则退 1441
上一篇: Effective Java笔记:复合优先于继承
下一篇: Effective Java笔记:接口优于抽象类
Jooou
博客等级 码龄9年 226粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值