Effective Javax学习笔记--使可变性最小

news2024/9/23 2:33:51

这里正式进入第4章“类与接口”,其中第15和16条主要涉及类的封装,相关内容在Code Complete的第六章已经有了较为详细的描述,因此就不再重复了,直接从第17条开始。首先说一下为什么类要保证可变性最小。

为什么要使得类的可变性最小

确保类的可变性最小是面向对象设计中的一项重要原则,它不仅有助于提升代码的质量,还能增强系统的稳定性和可维护性。主要体现在下面几方面:

  • 线性安全性:可变类在多线程的环境下是线程不安全的,需要设置一定的线程安全措施
  • 类的可变性越大其被错误篡改的可能性越大,而不可变类没有这个问题可以被自由的共享。
  • 减少错误提高可靠性:类的可变性越大,其状态就越容易受到外部因素的影响,从而增加了出错的机会。不可变类由于其状态固定,可以避免意外的修改,减少了潜在的错误来源。此外,不可变对象的行为更加可预测,因为它们不会因为外部环境的变化而改变,这使得系统整体更加可靠和稳定。
  • 提升封装性:最小化可变性意味着更严格的封装。通过限制对对象状态的直接访问和修改,可以更好地控制对象的生命周期和行为。私有成员变量和只读访问(如通过getter方法)有助于保护对象的内部状态,防止不当的修改。这种封装性进一步增强了对象的独立性,也简化了对象间的交互。
  • 适用于缓存和共用:不可变类非常适合缓存和重用,因为它们的状态永远不会改变。这意味着一旦创建了不可变对象,就可以安全地在不同的上下文中多次使用,而不用担心数据的时效性。这种特性在性能敏感的应用场景中尤其有价值,如在高并发系统中,可以减少不必要的对象创建和垃圾回收开销。

所以只要没有理由,就需要把类的各项元素尽可能地设置为不可变,而这方面的极致就是不可变类(类中所有成员在初始化后都不可变)。

什么是不可变类

对于被判断无需进行不可变类是指其实例在初始化以后不可以被修改的类。不可变类的每一个实例的所有信息在创建该实例的时候都应该被提供,并在整个生命周期中固定不变。那要符合什么原则才能够真正保证类的不可变呢?文中列出了5点:

  • 不要提供任何会修改对象状态的方法:重点就是setter方法。
  • 保证类不会被扩展:如果类允许被扩展,则子类可以通过改写父类的相关方法破坏其不可变性。
  • 声明所有域都是final的:所有内部成员的引用在指向成员的初始化值后不能再被更改。
  • 声明所有的域都是私有的:外部无法访问修改成员变量
  • 确保对于任何可变组件的访问有且只有一个(在类的内部):不能直接向外部的引用返回内部私有类实例的引用,这样会使得内部可变实例的引用有内外部两个。

这几条原则里,有几条需要重点说明一下:

  • 第2条其实有两种实现方式,一是直接添加final关键字,二是将构造函数设为私有,通过静态工厂方法来初始化类,这样由于子类无法通过父类的构造方法来初始化父类的属性,所以也可以实现禁止扩展的目的。
  • 第3条其实有些过于强硬了,实际的要求应该是没有一个方法可以对对象的状态产生外部可见的改变。这里可以允许一些开销昂贵的域设置为非final,当第一次请求执行相关计算的时候将结果缓存在这些域中,等到后续再次计算时就不需要重新计算而是可以直接返回缓存的值,从而节约了计算的开销。由于不可变对象整体是不可变的,这也保证了相关的非final域无法被改变。
  • 第5条是对于类方法提的要求,需要通过在子程序设计中不对实例进行修改而是返回新的对象来实现。这里举一个例子:
public final class complexNumber{
    private final double real;
    private final double imaginary;

    public double getReal(){
        return this.real;
    }

    public double getImaginary(){
        return this.imaginary;
    }

    public complexNumber(double real, double imaginary){
        this.real = real;
        this.imaginary = imaginary;
    }

        public complexNumber add(complexNumber o){
            return new complexNumber(this.real + o.real, this.imaginary + o.imaginary);
        }

        public complexNumber subTract(complexNumber o){
            return new complexNumber(this.real-o.real, this.imaginary-o.imaginary);
        }

        public complexNumber multiply(complexNumber o){
            return new complexNumber(this.real*o.real - this.imaginary*o.imaginary, this.real*o.imaginary + this.imaginary*o.real);
        }

        
        public complexNumber divide(complexNumber o){
            if(o.real==0||o.imaginary==0){
                throw new IllegalArgumentException("Cannot divide by zero");
            }
            return new complexNumber(this.real/o.real, this.imaginary/o.imaginary);
        }
}

这里对于ComplexNumber的四则运算并未对原有实例的成员变量进行修改,同时也未直接返回内部成员变量的引用,而是返回一个新的ComplexNumber,这就维护了类的不可变属性。

不可变类的优势

在文章开头已经初步说明了不可变性的优势比较抽象,这里会具体说说使用不可变类带来的好处。

  • 不可变对象本质上是线性安全的,不要求做同步:这个上文已经说了。
  • 不可变对象可以自由的共享,甚至可以共享他们的内部信息:前半句话没有问题,主要是后半句话我认为作者的意思是在新建不可变类的其他实例时,可以安全的复用原实例中的内部信息(如作者举的BigInteger的negate方法例子),而不是可以对外直接共享内部信息(这与上文不可变类的第5个原则冲突)。
  • 不可变对象为其他对象提供了大量的构件:我们知道对象的不可变性除了通过final关键字来限制引用不变以外,更为重要的是要限制实例的可变性。不可变对象就可以保证实例的不可变性,所以对于各种集合类等包含了其他对象的类就可以把不可变对象作为基础的构件。
  • 不可变对象无偿提供了失败的原子性:不可变类的状态永远不变,因此不存在临时不一致的可能性。

不可变类的劣势

不可变类的唯一一个缺点就是对于每一个不同的值都需要一个单独的对象。这对于一些重量级类来说是非常浪费资源和性能的。这里作者提出了一种解决方法--可变配套类。

可变配套类

可变配套类顾名思义就是不可变类的一个辅助类,它具有可变的属性,协助匹配的不可变类提升运行效率。

我们知道当不可变类的类方法是一个多步骤操作的时候,如果完全通过不可变类的方法来执行,理论上每一个步骤都会产生一个新的不可变对象,但是在这个操作结束以后,所有过程步骤中的不可变对象又会被删去,这就极大的影响了操作的执行效率。这里如果可以将过程步骤的产出改为基本类型输出则最好,如果不行的话就需要新设置一个包级私有的可变配套类来执行这些过程步骤,最后如果相关的过程步骤完全不可预测时,可以所幸设置一个公有的可变配套类,在需要执行多步骤操作的时候直接通过可变类来实现而非相匹配的不可变类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1944569.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HackTheBox--Knife

Knife 测试过程 1 信息收集 端口扫描 80端口测试 echo "10.129.63.56 knife.htb" | sudo tee -a /etc/hosts网站是纯静态的,无任何交互功能,检查网页源代码也未发现任何可利用的文件。 检查页面请求时,请求与响应内容&#xff0…

如何避免蓝屏?轻量部署,安全和业务连续性才能两不误

自19日起,因CrowdStrike软件更新的错误配置而导致的“微软全球蓝屏”,影响依然在持续。这场被称为“史上最大规模的IT故障”,由于所涉全球企业太多,专家估计“蓝屏”电脑全部恢复正常仍需时日。 尽管 CEO 乔治 库尔茨&#xff08…

Kali中docker与docker-compose的配置

权限升级 sudo su 升级为root用户 更新软件 apt-get update安装HTTPS协议和CA证书 apt-get install -y apt-transport-https ca-certificates安装pip apt-get install python3-pip查是否成功安装 pip3 -V or pip3 --version 下载docker apt下载docker apt install doc…

【故障排查】Docker启动Nacos报错:No DataSource set 问题解决

Nacos报错内容 Nacos Server did not start because dumpservice bean construction failure : No DataSource set原因分析 Nacos 配置的是单机模式,使用mysql 进行存储配置文件,Nacos的启动脚本已经配置了MySQL的连接方式,根据错误提示&a…

麒麟V10安装nginx、mysql报错缺少包:error while loading shared libraries libssl.so.10

背景 启动nginx报错:error while loading shared libraries libssl.so.10 解决 查看nginx启动文件所依赖的动态链接库(即共享库或动态库) ldd nginx-1.22.1/sbin/nginx离线安装compat-openssl10包 将依赖包麒麟v10安装openssl10依赖包上…

MYSQL存储引擎InnoDB, MyISAM简介

MYSQL存储引擎 在开始谈到mysql存储引擎之前,我们应该知道或者了解存储引擎是什么,存储引擎是为了解决什么样的问题的。 在mysql中,存储引擎是处理不同表类型SQL操作的MySQL组件,同时MySQL服务器采用可插拔的存储引擎架构&#x…

关于Qt部署CMake导致“Failed to set working directory to”的问题

2024年7月23日补充:该目录过深的情况只在Win10上有发现,Win11则没有问题,且Win11可以在DevHome中设置LongPath。 --------------------------------------------------------------------------------------------------------------- 使用qt…

ABAP+从SAP发出去的PDF文件在第三方系统出现乱码

这是一个 ABAP转换PDF调用函数CALL FUNCTION CONVERT_OTF的问题记录,关乎字体STSong-Light-ldentity-H 和 STSong-Light的区别 背景: 做了一个增强,是采购订单审批后自动发送采购订单PDF1到企业微信,用户再将企业微信收到的P…

【独立站运营经验分享】独立站8大运营模式怎么选?

跨境独立站模式众多,你了解多少呢?今天,带你充分了解各种模式优劣对比,从知道适合做什么样的独立站。 1、铺货模式 指不局限于单一品类,广泛、批量地上架市场反响好的商品模式,以大量的SKU和较低的价格为特…

基于 Electron+Vite+Vue3+Sass 框架搭建

技术参考 技术描述Electron一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。嵌入 Chromium 和 Node.jsElectron Forge用于打包和分发 Electron 应用程序的一体化工具。英文地址在此Vite前端构建工具Vue3用于构建用户界面的 JavaScript 框架vitejs/plugin-vueVite 插…

鸿蒙OS物联网创新应用实训解决方案

摘要: 随着物联网技术的飞速发展,各种智能设备和传感器正在以前所未有的速度融入我们的日常生活。华为推出的鸿蒙操作系统(HarmonyOS)作为一款面向全场景、多设备、无缝连接的分布式操作系统,为物联网领域带来了全新的…

pdf文件压缩的有效方法,详解5个效果高效的文件压缩方法汇总!

在现代信息社会中,PDF 文件已经成为我们日常工作和学习中不可或缺的重要载体。然而,随着 PDF 文件内容的增多和复杂化,文件大小的膨胀也成为一个常见问题,给存储、共享和传输带来了不少挑战。本文旨在探讨如何通过有效的压缩方法来…

VScode 修改 Markdown Preview Enhanced 字体以及大纲编号

修改字体和背景颜色 按快捷键 Ctrl , 打开设置,搜索 markdown-preview-enhanced.previewTheme,选择一个黑色主题的css,如 github-dark.css. 修改自动编号和背景颜色 背景颜色 按 F1 或者 Ctrl Shift P,输入 Customize CSS…

WEB 手柄 http通信,mcu端解析代码 2024/7/23 日志

WEB 手柄 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>WEB遥控器</title> </head> &l…

【Linux】条件变量及生产者消费者模型

为什么要将这两者放在一起进行呢&#xff1f; 主要是因为生产消费与条件变量关系密切&#xff0c;正好相辅相成。 目录 条件变量&#xff1a;条件变量的引出&#xff1a;条件变量的解释与接口&#xff1a;测试代码&#xff1a; 生产者消费者模型&#xff1a;概念&#xff1a;代…

Window版本nginx修改文件访问句柄数被限制,解决大并发量访问时无法响应问题

目录 一、问题背景 二、问题分析 三、解决办法 四、查看nginx连接状态 一、问题背景 Windows版本因为文件访问句柄数被限制为1024了&#xff0c;当大并发量访问时就会无法响应。会有如下错误提示&#xff1a;maximum number of descriptors supported by select() is 1024…

强化学习学习(二)基于价值函数就能做到RL——Q-Learning Q学习

文章目录 Value funtion methods-为什么我们用回了Q函数&#xff1f;Q-iterationQ-Learning (P30) Value funtion methods-为什么我们用回了Q函数&#xff1f; 先回顾一下在AC中的基于V函数的框架&#xff1a; 另一个想法&#xff1a;不依赖梯度&#xff0c;而是直接根据值函…

vue3+vite 实现动态引入某个文件夹下的组件 - glob-import的使用

<template><div class"user-content"><HeaderTitle title"用户详情"></HeaderTitle><div class"main-content"><div><UserForm /></div><div><TableList></TableList></d…

pytest实战技巧之参数化应用

pytest是Python中最流行的测试框架之一。它提供了丰富的功能&#xff0c;可以帮助我们编写高效、可靠的测试用例。其中一个重要的功能就是参数化&#xff0c;它可以让我们用不同的数据组合来运行同一个测试用例&#xff0c;从而 提高测试覆盖率和效率。本文将介绍pytest参数化的…

python之名称空间和作用域(关键字:global和nonlocal的使用)

文章目录 前言1、名称空间和作用域1.1 引言1.2 名称空间1.2.1 内置名称空间1.2.2 全局名称空间1.2.3 局部名称空间1.2.4 名称空间的产生和销毁顺序 1.3 作用域1.3.1 全局作用域1.3.2 局部作用域1.3.3 名字的查找顺序 1.4 关键字&#xff1a;global1.5 关键字&#xff1a;nonloc…