Effective Java笔记:使可变性最小化

MapGIS地质剖面图绘制实战:从数据准备到整饰出图 本文详细解析了MapGIS地质剖面图绘制的全流程,从数据准备到整饰出图,涵盖了等高线处理、GRD格网化、比例尺控制等关键步骤。通过实战技巧和案例分析,帮助地质工作者高效完成专业地质剖面图绘制,提升矿产勘探和工程勘察的工作效率。 阅读详情

使可变性最小化

在面向对象编程中,最小化可变性(Immutability) 是一种重要的设计原则。不可变对象(Immutable Object)是指其状态在对象创建后不能被更改的对象。不可变对象具有简单性、健壮性和线程安全性。

《Effective Java》中明确指出,不可变对象的设计是代码安全性和可维护性的基础。在很多场景下,我们应尽可能减少类的可变性,甚至设计为完全不可变。如果类必须是可变的,则需要限制其可变范围。


1. 为什么要最小化可变性?

  1. 线程安全性(Thread-Safety)

    • 不可变对象是天然的线程安全的,多个线程可以同时访问它而无需额外同步,减少了并发编程的复杂性。
  2. 易于设计和使用(Simpler Design)

    • 不可变对象的 API 更加简单和安全,并且可以防止调用者对不应修改的字段进行更改。
  3. 提高代码的安全性

    • 减少了由于对象状态修改带来的潜在问题,例如字段被外部代码意外或恶意篡改的风险。
  4. 更容易维护

    • 不需要担心状态的变化影响行为,可以减少调试和维护成本。
  5. 缓存友好

    • 不可变对象的属性不会发生变化,因此可以被安全地用作缓存的键值。

2. 如何设计不可变类?

要点:设计不可变类需要遵循以下原则
  1. 将类声明为 final,防止子类继承和扩展

    • 子类可能会引入可变状态,破坏不可变性。
  2. 将所有的字段都声明为 private 和 final

    • private 确保字段不能被直接访问,final 确保字段一旦初始化后不能被重新赋值。
  3. 不要提供修改对象状态的方法(没有 setter 方法)

    • 不允许通过外界调用来更改字段值。
  4. 确保对象的防御性拷贝

    • 如果类包含可变对象(如数组、集合),不要直接暴露这些字段,应该返回这些对象的副本,而不是返回其引用或原始对象。
  5. 在构造器中构造对象的全部状态

    • 确保对象在构造之后处于完全初始化状态。
  6. 确保类的字段引用的对象也不可变

    • 如果一个类的字段引用了另一个可变对象,必须将其设计为不可变,或者在调用者访问时提供拷贝。

示例:一个不可变的 Person 类
public final class Person {
    private final String name;  // 不可变字段
    private final int age;

    public Person(String name, int age) {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }

        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;  // 不需要返回副本,因为 String 是不可变的
    }

    public int getAge() {
        return age;
    }
}

上面这个 Person 类不可变,因为:

  1. 类被声明为 final。
  2. 所有字段都为 private 和 final。
  3. 没有提供任何修改字段的方法(没有 setter)。
  4. 字段 name 是 String 类型,而 String 是不可变的。

3. 不可变类设计的优势

不可变类有以下优势:

3.1 线程安全性

不可变对象的状态不能被更改,因此可以安全地在线程之间共享,无需显式同步。

例子:

Person person = new Person("Alice", 25);
Thread thread = new Thread(() -> System.out.println(person.getName()));
thread.start();

多个线程可以安全地共享同一个不可变对象,无需担心数据竞争。


3.2 更易于理解和使用

不可变对象提供了一种明确的行为保证:它们的状态不会发生变化。这使 API 的使用更加直观。


3.3 较少的 Bug 概率

当对象的状态是不可变的时候,程序中许多异常状态根本不会发生。例如,不可变对象不会因为意外修改而进入不一致的状态。


3.4 安全的数据传递

不可变对象可以安全地作为参数传递至其他方法,而不需要担心它会被方法内部修改。


4. 使可变类的可变性最小化

问题:必须是可变类时,如何最小化其可变性?

尽管不可变类是优选,但在某些场景中,我们需要使用可变类(如对象需要频繁更新)。对于这样的类,我们可以通过以下方式最小化其可变性:

4.1 将类的字段设置为 private

确保类的字段不会被外部直接访问。只能通过控制良好的方法来修改它们。


4.2 使用访问方法控制修改逻辑

通过设计良好的访问方法(setter 和 getter),增加对字段的验证、约束和访问权限。

public class Account {
    private double balance;

    public Account(double balance) {
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }

    public void deposit(double amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("Deposit amount cannot be negative");
        }
        balance += amount;
    }
}

4.3 不要直接暴露字段引用

如果类中包含可变对象,不要将这些引用直接公开,而应提供不可变的视图或防御性拷贝。

错误示范:直接暴露字段

public class Mutable {
    public int[] data = {1, 2, 3}; // 公共字段
}

外部可以直接修改字段值:

Mutable mutable = new Mutable();
mutable.data[0] = 42;  // 修改了原数组

正确做法:返回副本

public class Safe {
    private final int[] data = {1, 2, 3};

    // 返回数组副本
    public int[] getData() {
        return data.clone(); 
    }
}

4.4 限制可变对象的作用域

如果某些字段是可变的,尽可能缩小它们的可见性。可以通过同步或其他机制保护它们不被滥用。


5. 实际代码示例

不可变类示例:Money 类(不可更改金额值)
public final class Money {
    private final double amount;
    private final String currency;

    public Money(double amount, String currency) {
        if (amount < 0) {
            throw new IllegalArgumentException("Amount cannot be negative");
        }
        this.amount = amount;
        this.currency = currency;
    }

    public double getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }

    // 返回新的 Money 对象
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currencies do not match!");
        }
        return new Money(this.amount + other.amount, this.currency);
    }
}

优点:

  1. Money 类是不可变的:没有 setter 方法,也没有状态变化。
  2. 增加金额时,返回一个新的 Money 对象,而不是修改原对象。

可变类的最小化示例:学生类
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        if (name == null || name.trim().isEmpty()) {
            throw new IllegalArgumentException("Name cannot be empty");
        }
        this.name = name;
    }

    public void setAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
        this.age = age;
    }
}

6. 总结

  1. 对于需要经常共享或操作的类,尽量设计成不可变类。
  2. 如果类既是可变的,尽量缩小可变范围:
    • 使用 private 修饰字段,限制其可见性。
    • 提供安全的访问方法和必要的验证逻辑。
    • 返回字段副本,防止外界意外修改。
  3. 不可变的设计可以提升线程安全性、降低 bug 风险,并提高代码维护性。

核心原则:默认设计为不可变类,只有确有必要时才设计为可变类,且最小化其范围。

PMSM控制系列文章进阶-8.电流、角度采样及其同步 摘要:本文分析了高低压电机在电流和角度采样中的不同方案选择。高压电机采用霍尔传感器和旋变进行隔离采样,低压电机则使用shunt电阻和磁编码器以降低成本。重点讨论了采样时间补偿问题:电流采样需与PWM周期同步,角度采样需考虑传感器延迟;通过park变换和反park变换时需补偿采样与发波的时间差,提出了通过测试母线电流或Ud变化来验证补偿时间准确性的方法。文中还介绍了对传感器信号进行软解码处理的方法,包括消除直流偏置、增益误差和正交误差等参数的自学习过程。 阅读详情

相关推荐

从叶片之舞到数据之眼:净空雷达如何重塑风电安全与效率的边界

净空雷达通过激光或毫米波技术实现风机叶片与塔筒间距离的毫米级实时监测,极大提升了风电场在极端天气下的安全性与运营效率。这项技术不仅预防扫塔事故,还通过数据可视化与智能分析赋能叶片健康管理和发电优化,成为风电数字化运维的核心基础设施。

xk7n92h3p5m的博客 1161

《Effective Java》阅读笔记 15使可变性最小化

1.不可变类的优势 不可变类只是其实例不能被修改的类。不可变的类比可变类更加易于设计、实现和使用。它们不容易出错,且更加安全。 Java 平台类库包含许多不可变的类,包括 String 类、基本类型包装类以及 BigInteger 类和 BigDecimal 类。 有很多很好的理由:不可变类比可变类更易于设计,实现和使用。 他们不容易出错,并且更安全。 2.如何设计不可变类 1.不要提供任何会修改对象状态的方法。 2.保证类不会被扩展。 3.使所有的域都是 final 的。 4.使所有的域都是私有的。 5.

weixin_41563161的博客 355

使用libx265对YUV进行h265编码

使用libx265对YUV进行h265编码

Embedded engineer 3079

Effective Java笔记(17)使可变性最小化

与之相对应的更常见的是过程的(procedural)或者命令式的( imperative )方法,使用这些方法时,将一个过程作用在它们的操作数上, 会导致它的状态发生改变。不可变的类变成 final 的另 一种办法就是,让类的所有构造器都变成私有的或者包级私有的,并添加公有的静态工厂( static factory )来代替公有的构造器(详见第 1 条)。对于处在包外部的客户端而言,不可变的类实际上是 final 的,因为不可能对来自另一个包的类 、 缺少公有的或受保护的构造器的类进行扩展。

琅琊之榜 262

Python模拟发送SOME/IP消息(Scapy模块)

SOMEIP提供服务、订阅、响应、通知等Python代码示例

liluo0815481的博客 3068

网络变压器等效电路及网络变压器中间抽头作用详解

       华强盛电子导读:要详细了解网络变压器周边EMC管控及网络变压器BOB-SMITH电路的连接原理,首先我们对网络变压器的认识及其电路各参数影响都要有个简要的认识,本文,对非理想网络变压器等效电路做一个详细的分析,其中重点讨论一下网络变压器中间抽头的作用其影响                          网络变压器等效电路及                        ...

网络变压器专业资料 6006

《Effective Java》读书笔记 - 最小化类的可变性

Item 15 最小化类的可变性 effective java 如何做到让类不可变? 不提供改变对象状态的方法。(mutators) 确保类不会被继承,即用final来限定类。 让所有的域(field)为final。 让所有的域private。 确保所有对可变组件的互斥访问(Ensure exclusive access to any mutable components) 例子...

arkrszji40041的博客 198

Matlab:imageDatastore() 高效读取图像数据集的实战技巧

本文详细介绍了Matlab中imageDatastore()函数的高效使用技巧,帮助用户从繁琐的手动读取图像数据集中解放出来。通过核心语法、关键参数(如IncludeSubfolders、LabelSource、ReadSize)的解析,以及数据集分割、打乱等进阶实战操作,指导读者构建稳定、高效的图像数据读取管道,特别适用于大型图像数据集处理和机器学习项目。

hill8的博客 1100

Effective Java读书笔记-使可变性最小化

存在不可变类的原因:不可变的类比可变的类更加易于设计、实现和使用。它们不容易出错且更加安全。使类变为不可变需要遵守的五项原则: 不要提供任何会修改对象状态的方法。 保证类不会被拓展。这样可以防止粗心或者恶意的子类假装对象的状态已经改变,从而破坏类的不可变行为。为了防止子类化,一般的做法是使类变为final的。 使所有的域都是final的。 使所有的域都变为私有的。 确保任何可变组件的互斥访问。如果类

G7S9R的专栏 472

ubuntu18.04安装Nvidia显卡驱动后黑屏及网络、蓝牙驱动消失的解决方案

ubuntu18.04安装Nvidia显卡驱动后黑屏及网络、蓝牙驱动消失的解决方案

weixin_40735291的博客 6618

innovus:addRepeaterByRule用法

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?addRepeaterByRule不管transition/timing violation,只根据约束的length/cap/fanout等等去给net 插buffer。

m0_61544122的博客 1091

Effective Java笔记第三章类和接口第三节使可变性最小化

Effective Java笔记第三章类和接口 第三节使可变性最小化 1.不可变类只是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变。6 2.不可变类需要遵守的规则: 1)不要提供任何会修改对象状态的方法。 2)保证类不会被扩展。这样可以防止粗心或者恶意的子类假装对象的状态已经改变,从而破坏该类的不可变行为。为了防止子类化,一般做法是使这个类成为final的。 3)使所有的域都是final的。通过系统的强制方式,这可以清楚地表明你的意图。而且,

zh123456_789的博客 190

【CAN总线测试】——通讯相关诊断测试

本章主要介绍通讯相关诊断测试用例设计

LOVE135149的博客 1815

Effective Java 读书笔记——15:使可变性最小化

不可变类 不可变类是它的实例不能被修改的类。每个实例中所有的信息,必须在创建的时候提供,并在其整个对象周期内固定不变,例如:String,基本的包装类,BigInteger和BigDecimal。不可变的类更加的易于设计、实现和使用。它们不容易出错,而且更加安全。 为了使类成为不可变的,一般遵循以下几个原则: 不要提供任何会修改对象状态的方法(改变对象属性的方法,也称为mut

萌萌小七的专栏 612

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Effective Java 第二版 中文版 笔记(十五)使可变性最小化

不可变类只是其实例不能被修改的类。 即每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变。 为了使类成为不可变,要遵循下面五条规则: 不要提供任何会修改对象状态的方法 保证类不会被扩展 使所有的域都是final的 使所有的域都成为私有的 确保对于任何可变组件的互斥访问。 public final class Complex{ priva...

qq_37538698的博客 158

jre9下载 jdk9

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

effective Java 学习笔记(第二弹)

整理自《effective Java 中文第3版》本篇笔记整理第3,4章的内容。

Willow的博客 1168

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

《Effective Java》第17条:使可变性最小化

原文P69:许多不可变的类拥有一个或者多个非 final的域,它们在第一次被请求执行这些计算的时候,把一些开销昂贵的计算结果缓存在这些域中。声明所有的域都是 final的。而且,如果一个指向新创建实例的引用在缺乏同步机制的情况下,从一个线程被传递到另一个线程就必须确保正确的行为,即必须是线程安全的。当然,如果想让一个类成为“不可变类”,方法不仅仅有 使类是 final 的,还有另一种更灵活的方式:让类的所有构造器都变成私有的或者包级私有的,并添加公有的静态工厂来代替公有的构造器。声明所有的域都为私有的。

TD_FAlmK的博客 505

《Effective Java》解读第17条:使可变性最小化

摘要:文章探讨了不可变类的概念与实现方法。不可变类指实例化后状态不可修改的类,如String和基本包装类。实现要点包括:不提供修改方法、定义final类、声明final私有字段、防御性拷贝可变组件。优点包括线程安全、对象共享、构建其他对象等,缺点是每个不同值需单独对象。解决方案是提供可变配套类(如StringBuilder)。建议优先选择不可变类,必要时限制可变性,字段尽量设为private final。实际项目中虽少用final定义不可变类,但final常量很常见。

Cosmoshhhyyy的博客 305

Effective Java笔记:使类和成员的可访问性最小化

即通过隐藏实现细节,仅暴露必要的接口,来降低模块之间的耦合性。在设计公共类时,需要特别小心它的 API,因为一旦发布,修改它可能会破坏跟它交互的代码。在声明类和成员时,尽可能限制其访问权限,使其可见性最小化。字段是不可变的,但它仍然破坏了封装,因为类的实现细节(如错误代码字符串)对外公开。修饰的数组或集合使得对内部数据的修改权限不受控制,这是一个常见的过失设计。实现类(如接口的具体实现)通常是特定于模块的细节,不应直接暴露给外部。,允许子类复用父类的逻辑,但仍需谨慎设计受保护的成员。

eclipseC的博客 1152

Effective Java读书笔记

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

逆水行舟,不进则退 1441

带你快速看完9.8分神作《Effective Java》—— 类和接口篇

???? Java学习:Java从入门到精通总结 ???? Spring系列推荐:Spring源码解析 ???? 最近更新:2022年1月7日 ???? 个人简介:通信工程本硕????、阿里新晋猿同学????。我的故事充满机遇、挑战与翻盘,欢迎关注作者来共饮一杯鸡汤 ???? 点赞 ???? 收藏 ⭐留言 ???? 都是我最大的动力! 15 使类和成员的可访问性最小化 面向对象语言设计时的两大“法宝”就是封装和解耦。其中,封装可以隐藏组件所有的实现细节,把API与实现清晰隔离开,因此也就解除组成系

如切如磋,如琢如磨 1957

《Effective Java》读书笔记三

一、明确接口的核心作用定义类型:接口的主要目的是为了定义一种特定的行为规范和类型标识。它描述了一组方法签名,任何实现该接口的类都必须提供这些方法的具体实现。通过接口,我们可以以一种统一的方式来处理不同的实现类,而无需关心具体的实现细节。类型标识的重要性:接口作为一种类型,可以在代码中用于变量声明、方法参数和返回值类型等。这使得代码更加灵活和可扩展,因为可以在运行时根据实际情况传入不同的实现类,而无需修改调用代码。二、避免不恰当的使用不要在接口中定义常量以外的变量:接口不应该被用作存储数据的地方。

qq_41831136的博客 803

Effective Java 使可变性最小化

不可变类是实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变。Java平台包含的不可变类:String、基本类型的包装类、BigInteger、BigDecimal。不可变类比可变类更容易设计、实现和使用。 为了使类成为不可变,要遵循五条规则: 1.不要提供任何会修改对象状态的方法。 2.保证类不会被扩展,为了防止恶意子类假装对象状态被改变,从而破坏不可变。 3.使所有的域都是final的。 4.使所有的域都成为私有的,防止客户端获得.

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

Effective Java:使可变性最小化

1.什么是不可变类 需要的所有参数必须在实例化的时候都传进去。 对象中所有信息在对象的整个生命周期中都保持不变。 2.使类不可变的原则 不要提供任何修改对象状态的方法。 保证类不会被继承。 使所有的域都是final类型的。 使所有的域都是私有类型的。 确保对于任何可变组件的互斥性。 即,确保在该类的外部不会获取到该类中可变对象的引用。比如下面这个例子: public class MyObj...

添仔哥哥 504

Effective Java 第三版 中文翻译

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

Coloured_Glaze的博客 3861

Effective java笔记-第四章

类和接口第十三条使类和成员的可访问性最小化应该使用与你正在编写的软件的对应功能相一致的、最小的访问级别 对于顶层的类(顶层是指非嵌套的类)和接口只有两种可能的访问级别:包级私有和公有的。 如果类或接口可以被做成包级私有的就应该做成包级私有的,这样这个类实际成了这个包的实现的一部分,可以在以后对他修改,替换而不用担心影响到客户端程序,如果做成公有的,你就有责任永远支持它的兼容性。 如果一个包级

hjvgf的博客 826
上一篇: 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、付费专栏及课程。

余额充值