带你快速看完9.8分神作《Effective Java》—— 方法篇

2023-11-10 06:40

本文主要是介绍带你快速看完9.8分神作《Effective Java》—— 方法篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🍊 Java学习:Java从入门到精通总结

🍊 Spring系列推荐:Spring源码解析

📆 最近更新:2021年12月16日

🍊 个人简介:通信工程本硕💪、阿里新晋猿同学🌕。我的故事充满机遇、挑战与翻盘,欢迎关注作者来共饮一杯鸡汤

🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力!

豆瓣评分9.8的图书《Effective Java》,是当今世界顶尖高手Josh Bloch的著作,在我之前的文章里我也提到过,编程就像练武,既需要外在的武功招式(编程语言、工具、中间件等等),也需要修炼心法(设计模式、源码等等)学霸、学神OR开挂。

我也始终有一个观点:看视频跟着敲代码永远只是入门,从书籍里学到了多少东西才决定了你的上限。

在这里插入图片描述

我个人在Java领域也已经学习了近5年,在修炼“内功”的方面也通过各种途径接触到了一些编程规约,例如阿里巴巴的泰山版规约,在此基础下读这本书的时候仍是让我受到了很大的冲激,学习到了很多约定背后的细节问题,还有一些让我欣赏此书的点是,书中对于编程规约的解释让我感到十分受用,并愿意将他们应用在我的工作中,也提醒了我要把阅读JDK源码的任务提上日程。

最后想分享一下我个人目前的看法,内功修炼不像学习一个新的工具那么简单,其主旨在于踏实,深入探索底层原理的过程很缓慢并且是艰辛的,但一旦开悟,修为一定会突破瓶颈,达到更高的境界,这远远不是我通过一两篇博客就能学到的东西。

接下来就针对此书列举一下我的收获与思考。

不过还是要吐槽一下的是翻译版属实让人一言难尽,有些地方会有误导的效果,你比如java语言里extends是继承的关键字,书本中全部翻译成了扩展 就完全不是原来的意思了。所以建议有问题的地方对照英文原版进行语义上的理解。

没有时间读原作的同学可以参考我这篇文章。


文章目录

    • 49 检查参数的有效性
    • 50 必要时进行保护性拷贝
    • 51 谨慎设计方法
    • 52 慎用重载
    • 53 慎用可变参数
    • 54 返回空的数组或集合,不要返回null
    • 55 谨慎返回optional
    • 56 为所有已公开的API 元素编写文档注释

49 检查参数的有效性

当编写方法或构造方法时,都应该考虑其参数应该有哪些限制。应该把这些限制写到文档里,并在方法体的开头显式检查


大多数方法和构造方法对于传递给他们的参数有一些限制。例如,索引值必须是非负数,对象引用必须为非null。我们应该在文档里清楚地指明这些限制,并且在方法的最开始进行检查。

如果没有验证参数的有效性,可能会导致违背失败原子性

  1. 该方法可能在处理过程中失败,该方法可能会出现费解的异常
  2. 该方法可以正常返回,会默默地计算出错误的结果
  3. 该方法可以正常返回,但是使得某个对象处于受损状态,在将来某个时间点会报错

对于publicprotected方法,要用Java文档的@throws注解来说明会抛出哪些异常,通常为:IllegalArgumentExceptionIndexOutOfBoundsExceptionNullPointerException,例如:

/*** Returns a BigInteger whose value is (this mod m). This method* differs from the remainder method in that it always returns a* non-negative BigInteger.** @param m the modulus, which must be positive* @return this mod m* @throws ArithmeticException if m is less than or equal to 0
*/
public BigInteger mod(BigInteger m) {if (m.signum() <= 0)throw new ArithmeticException("Modulus <= 0: " + m);... // Do the computation
}

在Java 7中添加的 Objects.requireNonNull 方法灵活方便,因此没有理由再手动执行null检查。该方法返回其输入的值,因此可以在使用值的同时执行null检查:

this.strategy = Objects.requireNonNull(strategy, "strategy");

对于不是public的方法,通常应该使用断言来检查参数:

private static void sort(long a[], int offset, int length) {assert a != null;assert offset >= 0 && offset <= a.length;assert length >= 0 && length <= a.length - offset;... // Do the computation
}

不同于一般的有效性检查,如果它们没有起到作用,本质上也没有成本开销。


在某些场景下,有效性检查的成本很高,且在计算过程里也已经完成了有效性检查,例如对象列表排序的方法Collections.sort(List)

如果List里的对象不能互相比较,就会抛ClassCastException异常,这正是sort方法该做的事情,所以提前检查列表中的元素是否可以互相比较并没有很大意义。


有些计算会隐式执行必要的有效性检查,如果检查失败则会抛异常,这个异常可能和文档里标明的不同,此时就应该使用异常转换将其转换成正确的异常。


50 必要时进行保护性拷贝

Java是一门安全的语言,它对于缓存区溢出、数组越界、非法指针以及其他内存损坏错误都自动免疫。


但仅管如此,我们也必须保护性地编写程序,因为代码随时可能会遭受攻击

如果没有对象的帮助,另一个类是不可能修改对象的内部状态的,但对象可能会在无意的情况下提供这样的帮助。例如,下面的代码表示一个不可变的时间周期:

public final class Period {private final Date start;private final Date end;/*** @param start the beginning of the period* @param end the end of the period; must not precede start* @throws IllegalArgumentException if start is after end* @throws NullPointerException if start or end is null*/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;}... // Remainder omitted
}

上面代码虽然强制令period 实例的开始时间小于结束时间。然而,Date 类是可变的,很容易违反这个约束:

Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // Modifies internals of p!

从Java 8 开始,解决此问题的显而易⻅的方法是使用 Instant(或LocalDateTimeZonedDateTime)代替Date,因为他们是不可变的。但Date在老代码里仍有使用的地方,为了保护 Period 实例的内部不受这种攻击,可以使用拷⻉来做 Period 实例的组件:

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);
}

有了新的构造方法后,前面的攻击将不会对Period 实例产生影响。注意:保护性拷⻉是在检查参数的有效性之前进行的,且有效性检查是在拷贝实例上进行的

这样做可以避免从检查参数开始到拷贝参数之间的时间段内,其他的线程改变类的参数

也被称作 Time-Of-Check / Time-Of-Use 或 TOCTOU攻击


看了之前章节的同学可能有疑问了,这里为什么没用clone方法来进行保护性拷贝?

答案是:Date不是final的,所以clone方法不能保证返回类确实是 java.util.Date 的对象,也可能返

这篇关于带你快速看完9.8分神作《Effective Java》—— 方法篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/380979

相关文章

判断PyTorch是GPU版还是CPU版的方法小结

《判断PyTorch是GPU版还是CPU版的方法小结》PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIACUDA)上运行,所以对于深度学习开发者来说,正确识别PyTor... 目录前言为什么需要区分GPU和CPU版本?性能差异硬件要求如何检查PyTorch版本?方法1:使用命

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Java中的工具类命名方法

《Java中的工具类命名方法》:本文主要介绍Java中的工具类究竟如何命名,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java中的工具类究竟如何命名?先来几个例子几种命名方式的比较到底如何命名 ?总结Java中的工具类究竟如何命名?先来几个例子JD

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

SpringBoot整合OpenFeign的完整指南

《SpringBoot整合OpenFeign的完整指南》OpenFeign是由Netflix开发的一个声明式Web服务客户端,它使得编写HTTP客户端变得更加简单,本文为大家介绍了SpringBoot... 目录什么是OpenFeign环境准备创建 Spring Boot 项目添加依赖启用 OpenFeig

Java Spring 中 @PostConstruct 注解使用原理及常见场景

《JavaSpring中@PostConstruct注解使用原理及常见场景》在JavaSpring中,@PostConstruct注解是一个非常实用的功能,它允许开发者在Spring容器完全初... 目录一、@PostConstruct 注解概述二、@PostConstruct 注解的基本使用2.1 基本代

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删