Effective-Java-Chapter8-方法

news2025/2/5 15:02:26

https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/dev/Chapter-8/Chapter-8-Introduction.md
在这里插入图片描述

准则一 检查参数的有效性

  • 首先对于方法要写详细的文档,例如参数要求,抛出什么异常以源码为例:
    在这里插入图片描述
    又比如,我们可以通过注解的注解标记方法:
    在这里插入图片描述

  • 对于参数需要严格的校验,例如我们可以使用对象中的工具类

Objects.isNull();
Objects.nonNull();
Objects.requireNonNull()

又比如我们可以使用断言,书中更推荐非公共方法使用断言检查它们的参数:

Assert.notNull();
Assert.isTrue();

准则二 在需要时制作防御性副本

这个准则告诉我们如果我们对象中的数据是不可变的,那我们我们一定要保证它一定不可变,不能破坏它。什么意思呢?我们看书中的这个例子:

public final class Period {
	private final Date start;
	private final Date end;
}
  public Period(Date start, Date end) {
    if (start.compareTo(end) > 0)
        throw new IllegalArgumentException(start + " after " + end);
    this.start = start;
    this.end = end;
}

public Date start() {
    return start;
}

public Date end() {
    return end;
}

这个例子乍一看,看不出来有什么问题,感觉它就是安全的,但是其实由于Date是可变的,所以我们可以通过下面的方式改变它:

Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
// 我们改变了对象中的数据,对象中的数据并不是可变的
end.setYear(78);

于是有了这样的构造函数:

public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());
    if (this.start.compareTo(this.end) > 0)
        throw new IllegalArgumentException(this.start + " after " + this.end);
}

然后这样其实还是不安全的因为在get中,我们也可以拿到对象进行修改,于是我们有了这样的写法:

public Date start() {
    return new Date(start.getTime());
}

public Date end() {
    return new Date(end.getTime());
}

这样外面的怎么操作都是在副本上操作,绝对不会影响到我们真正的数据,保证了数据的安全性。
注意:

  • 我们没有使用 Date 的 clone 方法来创建防御性副本。因为 Date 不是 final 的,所以不能保证 clone 方法返回一个 java.util.Date 的实例对象:它可以返回一个不受信任子类的实例,这个子类是专门为恶意破坏而设计的。例如,这样的子类可以在创建时在私有静态列表中记录对每个实例的引用,并允许攻击者访问这个列表。这将使攻击者可以自由控制所有实例。为防止此类攻击,对可被不受信任方子类化的参数类型,不要使用 clone 方法进行防御性复制。
  • 我们不总是通过这种方法来保证我们数据的安全性,会影响性能,并且会额外开销内存。

准则三 仔细设计方法签名

  • 仔细设计方法名
  • 不要提供过于便利的方法。 每种方法都应该各司其职。太多的方法使得类难以学习、使用、记录、测试和维护。对于接口来说更是如此,在接口中,太多的方法使实现者和用户的工作变得复杂。对于类或接口支持的每个操作,请提供一个功能齐全的方法。只有在经常使用时才考虑提供便捷方式。但如果有疑问,就不要提供。
  • 避免长参数列表。 设定四个或更少的参数。
  • 对于参数类型,优先选择接口而不是类
  • 双元素枚举类型优于 boolean 参数, 除非布尔值的含义在方法名中明确。
    这个的意思是用枚举代替传入的布尔型变量。

准则四 明智地使用重载

  • 你总是可以为方法提供不同的名称,而不是重载它们。这个不是说不允许重载,而是重载以后可能会有不安全的问题难以发现,如果你确保重载的方法是完全没有歧义的,不会被误用。
  • 不要重载方法来在相同的参数位置上使用不同的函数式接口。
    方法可以重载,但并不意味着就应该这样做。通常,最好避免重载具有相同数量参数的多个签名的方法。在某些情况下,特别是涉及构造函数的情况下,可能难以遵循这个建议。在这些情况下,你至少应该避免同一组参数只需经过类型转换就可以被传递给不同的重载方法。如果这是无法避免的,例如,因为要对现有类进行改造以实现新接口,那么应该确保在传递相同的参数时,所有重载的行为都是相同的。如果你做不到这一点,程序员将很难有效地使用重载方法或构造函数,他们将无法理解为什么它不能工作。

准则五 明智地使用可变参数

在性能关键的情况下使用可变参数时要小心。每次调用可变参数方法都会导致数组分配和初始化。如果你已经从经验上确定你负担不起这个成本,但是你仍需要可变参数的灵活性,那么有一种模式可以让你鱼与熊掌兼得。假设你已经确定对方法 95% 的调用只需要三个或更少的参数。可以声明该方法的 5 个重载,每个重载 0 到 3 个普通参数,当参数数量超过 3 个时引入可变参数:

public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int a3) { }
public void foo(int a1, int a2, int a3, int... rest) { }

最佳实践:
EnumSet

准则六 返回空集合或数组,而不是 null

例如返回空集合:

return new ArrayList<>();

也可以引用同一个空列表来节约空间:

Collections.EMPTY_LIST

或者, 这样也是引用同一个列表:

private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

准则七 明智地的返回 Optional

  • 使用Optional的优势

    • 获取默认值的代价很高,除非必要,否则你希望避免这种代价。对于这些情况,Optional 提供了一个方法,该方法接受 Supplier,并仅在必要时调用它。
    • Optional 提供了一系列方法如 orElse(), orElseGet(), orElseThrow() 等,这些方法可以优雅地处理 null 值而不需要显式的 if-else 或 null 检查。
    • Optional 支持流式编程风格,可以方便地进行链式调用,比如 map() 和 flatMap() 方法可以用来转换或者组合多个 Optional 对象。
  • 永远不要从具备 Optional 返回值的方法返回空值: 它违背了这个功能的设计初衷。这句话显而易见,我们使用Optional是为了避免空指针的问题,但是直接返回空那还需要Optional 做什么呢。

  • 容器类型,包括集合、Map、流、数组和 Optional,不应该封装在 Optional 中。 你应该简单的返回一个空的 List,而不是一个空的 Optional<List>。

  • 那么,什么时候应该声明一个方法来返回 Optional 而不是 T 呢?作为规则,你应该声明一个方法来返回 Optional(如果它可能无法返回结果),如果没有返回结果,客户端将不得不执行特殊处理。 也就是说,返回 Optional 并不是没有代价的。Optional 对象必须分配和初始化,从 Optional 对象中读取值需要额外的间接操作。这使得 Optional 不适合在某些性能关键的情况下使用。某一特定方法是否属于这一情况只能通过仔细衡量来确定

  • 与返回基本数据类型相比,返回包含包装类的 Optional 类型的代价高得惊人,因为 Optional 类型有两个装箱级别,而不是零。因此,库设计人员认为应该为基本类型 int、long 和 double 提供类似的 Optional。这些可选类型是 OptionalInt、OptionalLong 和 OptionalDouble。它们包含 Optional 上的大多数方法,但不是所有方法。因此,永远不应该返包装类的 Optional,可能除了「次基本数据类型」,如 Boolean、Byte、Character、Short 和 Float 之外。注意这里是说的返回值。

  • 在集合或数组中使用 Optional 作为键、值或元素或这在构造函数中使用几乎都是不合适的。

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

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

相关文章

分享一个基于微信小程序的反诈科普平台springboot(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

Web开发:ORM框架之Freesql的入门和技巧使用小结

目录 零、官网链接 一、字段映射表 二、查询 1.freesql独特封装&#xff1a;between关键字 2.分页&#xff08;每页 20 条数据&#xff0c;查询第 1 页&#xff09; 3.Withsql&#xff08;子查询&#xff0c;不建议&#xff09; 3.简单查询、映射查询 4.参数查询、自定义…

RK3568笔记五十六:yolov8_obb旋转框训练部署

若该文为原创文章,转载请注明原文出处。 本文基于rknn_model_zoo和山水无移大佬的博客和代码训练模型并部署到正点原子的ATK-DLRK3568板子测试。 https://github.com/ultralytics/ultralytics 一、训练 1、环境搭建 使用的是AUTODL环境,yolov8-obb数据集不大,也可以使用c…

NIO中的异步—ChannelFuture、CloseFuture以及异步提升在NIO中的应用

ChannelFuture 客户端调用connect后返回值为ChannelFuture对象&#xff0c;我们可以利用ChannelFuture中的channel()方法获取到Channel对象。 由于上述代为为客户端实现&#xff0c;若想启动客户端实现连接操作&#xff0c;必须编写服务端代码&#xff0c;实现如下&#xff1a;…

TCP协议为什么是三次握手和四次挥手

1.一次握手&&二次握手 一次握手就能成功的话&#xff0c;也就代表着不需要进行确认&#xff0c;那么万一有恶意的服务器一直发送SYN&#xff0c;而服务器需要维护大量的连接&#xff0c;维护连接又需要成本&#xff0c;那么就很容易引发SYN洪水&#xff0c;导致服务器…

Linux中的exec族函数

exec 系列函数用于替换当前进程的用户空间代码和数据&#xff0c;从而执行一个新的程序。调用 exec 系列函数不会创建新的进程&#xff0c;但会用新程序的代码和数据替换当前进程&#xff0c;因此调用 exec 后&#xff0c;进程的 ID 保持不变&#xff0c;但进程的行为变为执行新…

计算机毕业设计 教师科研管理系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

第1章-05-通过浏览器控制台安装JQuery.js库

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年CSDN全站百大博主。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于专栏:Web爬虫入门与实战精讲。 🎉欢迎 👍点赞✍评论⭐收…

大数据背景下基于Python语言的单车租赁商业数据可视化分析

注&#xff1a;源码在最后&#xff0c;只是一次实验记录,不足之处请指教。 一 研究背景及意义 在大数据时代&#xff0c;商业领域的数据量迅速增长&#xff0c;如何有效地利用这些数据成为企业决策和优化成为重要的研究课题。单车租赁作为一种新兴的共享经济模式&#xff0c;其…

健韵坊(详细项目实战一)Spring系列 + Vue3

这一次来一个项目改造的项目实战&#xff0c;基于很久之前的一个demo项目&#xff0c;来实现一个改造优化和部署上线的项目实战。&#xff08;就当是接手*山项目并且加以改造的一个实战吧。&#xff09; 之前是一个关于运动的一个项目&#xff08;其实之前连名字都没想好hhhh&…

vue3 响应式 API:watch()、watchEffect()

watch() 基本概念 watch()用于监视响应式数据的变化&#xff0c;并在数据变化时执行相应的回调函数。可以监视单个响应式数据、多个响应式数据的组合&#xff0c;或者一个计算属性。 返回值 返回一个函数&#xff0c;调用这个函数可以停止监视。 特点 watch() 默认是懒侦听的&…

【Linux网络】select函数

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 select函数介绍select函数参数介绍select函数返回值select的工作流程TCP服务器【多路复用版】 select函数介绍 在Linux网络编程中&#xff0c;select 函数是一种非常有用的IO多路复用技术&#xff0…

秃姐学AI系列之:LeNet + 代码实现

目录 LeNet MNIST数据集 LeNet模型图 ​编辑 总结 代码实现&#xff1a;卷积神经网络 LeNet LeNet&#xff08;LeNet-5&#xff09;由两个部分组成&#xff1a;卷积编码器核全连接层密集块 检查模型 LeNet 卷积神经网络里面最为著名的一个网络&#xff0c;80年代末提出…

【vue教程】七. Vue 的动画和过渡

文章目录 往期列表回顾本章涵盖知识点Vue 的内置动画系统基本的进入和离开过渡列表过渡 CSS 过渡CSS 过渡基础Vue 中的 CSS 过渡 JavaScript 动画使用 JavaScript 钩子 第三方动画库的使用集成 Animate.css 实例演示创建一个简单的动画应用 结语 往期列表 【vue教程】一. 环境…

iOS18升级出现白苹果、无法重启等问题,要怎么解决

随着苹果iOS 18系统beta版本的推出&#xff0c;不少用户在私信说升级后遇到了白苹果和无法重启等问题。这些问题不仅影响了大家的正常使用&#xff0c;还会导致数据丢失和系统崩溃。本文将详细介绍iOS 18升级后出现白苹果、无法重启等问题的原因及解决方法&#xff0c;帮助大家…

日期转时间濯

tfunction(date_str) local code ,time World:getTimeFromDateString(date_str) return time/(60*60*24) end print(t(2024-08-16)-t(2024-08-3))

指针 (四)

一 . 指针的使用和传值调用 &#xff08;1&#xff09;strlen 的模拟实现 库函数 strlen 的功能是求字符串长度&#xff0c;统计的是字符串中 \0 之前的字符个数&#xff0c;函数原格式如下&#xff1a; 我们的参数 str 接收到一个字符串的起始地址&#xff0c;然后开始统计…

JS 获取当前操作系统类型

在JavaScript中&#xff0c;‌直接获取用户的操作系统信息是不可能的&#xff0c;‌因为JavaScript主要运行在浏览器中&#xff0c;‌而浏览器出于安全和隐私的考虑&#xff0c;‌不会提供访问操作系统详细信息的API。‌ 但是&#xff0c;‌你可以通过分析用户代理字符串&…

数据库性能定位-慢sql定位、sql优化(docker容器实战)

安装好mysql数据之后&#xff0c;创建库的时候&#xff0c;要注意选择 字符集编码。如果没有选择好&#xff0c;你的库表存中文的时候&#xff0c;会字符集乱码。选择utf8mb4. 建表的时候&#xff0c;存储引擎 InnoDB、MyISAM mysql5.7及以后数据库&#xff0c;表的默认存储引…

官方招募 | 仓颉语言三方库社区建设全速启航,全球开发者、技术大神只等您!

Cangjie-TPC招募令 仓颉社区的小伙伴们&#xff0c;官方三方库&#xff08;Cangjie-TPC&#xff09;招募开始啦&#xff01; Cangjie-TPC&#xff08;https://gitcode.com/Cangjie-TPC&#xff09; 是 Cangjie 社区用于汇集基于仓颉编程语言开发的开源三方库的主干仓&#xf…