自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(99)
  • 收藏
  • 关注

原创 《Effictive Java》读后感

如果《阿里巴巴》开发手册是对初学者的建议,那么这本书更像是对中级开发者的建议,该书应该说是《阿里巴巴》开发手册进一步的补充,尤其是中后部分方法、异常、序列化部分内容,在现实开发中容易踩的坑基本上都说到了,以及如何去避免,是一本质量非常高的书籍。大概前前后后花了八个月将这本书的内容以及知识点看完写完,整体上收获颇丰,但还有一些知识点比较难懂,比如泛型部分。第三版本有两个翻译,最新版本的翻译对读者来说更容易理解。后面有时间的话会进一步对内容进行修改,完善。

2024-12-14 13:02:44 120

原创 第九十条:考虑使用序列化代替序列化实例

首先,为可序列化的类设计一个私有的静态嵌套类,精确地表示外围类的实例的逻辑状态。在当前的OpenJDK的实现中,从客户端的角度来看,它们返回EnumSet实例,但是实际上,它们是返回两种子类之一,具体取决于底层枚举类型的大小。正如第85项和第86项中提到以及本章中所讨论的,决定实现Serializable接口,会增加bug和出现安全问题的可能性,因为它导致实例要利用语言之外的机制来创建,而不是用普通的构造器。换句话说,writeReplace方法在序列化之前,将外围类的实例转变成了它的序列化代理。

2024-12-10 21:31:21 675

原创 第八十九条:对于实例受控的类,首选枚举类型而不是readResolve

对于一个正在被反序列化的对象,如果它的类定义了一个readResolve方法,并且具备正确的声明,那么在反序列化之后,新建对象上的readResolve方法就会被调用。任何一个readObject方法,不管是显示的还是默认的,它都返回一个新建的实例,这个新建的实例不同于该类初始化时创建的实例。首先,编写一个“盗用者”类,它既有readResolve方法,又有实例域,实例域指向被序列化的Singleton的引用,“盗用者”类就“潜伏”在其中。如果它是受保护的或者公有的,就适用于所有没有覆盖它的子类。

2024-12-10 21:26:17 474

原创 第八十八条:保护性地编写readObject方法

通过伪造字节流,要想创建可变的Period实例仍然是有可能的,做法是:字节流以一个有效地Period实例开头,然后附加上两个额外的引用,指向Period实例中的两个私有的Date域。总而言之,每当你编写readObject方法的时候,都要这样想:你在编写一个公有的构造器,无论给它传递什么样的字节流,它都必须产生一个有效的实例。在正常使用中,字节流是通过序列化正常构造的实例生成的。当readObject面对一个人工生成的违反类的约束条件的字节流的时候,问题就出现了,它会生成一个违反类的约束条件的对象。

2024-12-10 21:19:31 677

原创 第八十六条:在实现serializable接口时要特别谨慎

当一个可序列化的类被修订的时候,很重要的一点是,要检查是否可以"在新版本中序列化一个实例,然后在旧版本中反序列化",反之亦然。这个自动产生的值会受到类名称、它所实现的接口的名称、以及所有公有的和受保护的成员的名称所影响。无论你是接受了默认的行为,还是覆盖了默认的行为,反序列化机制(deserialization)都是一个"隐藏的构造器",具备与其他构造器相同的特点。如果一个类实现了Serializable,它的字节流编码(或者说序列化形式,serialized form)就变成了它的导出的API的一部分。

2024-12-10 21:10:56 950

原创 第八十七条:考虑使用自定义的序列化形式

如果你正在使用默认的序列化形式,并且把一个或者多个域标记为transient,则要记住,当一个实例域被反序列化的时候,这些域将被初始化为它们的默认值(default value):对于对象引用域,默认值为null;对于StringList,默认的序列化形式不够灵活,并且执行效果不佳,但是序列化和反序列化StringList实例会产生对原始对象的忠实拷贝,它的约束关系没有被破坏,从这个意义上讲,这个序列化形式是正确的。在上面的例子中,序列化形式既表示了链表中的每个项,也表示了所有的链接关系,这是不必要的。

2024-12-10 21:08:16 506

原创 第八十五条:优先选择其他序列化替代方案

虽然程序猿承诺在分布式对象上付出点努力得到的成果是很有吸引力的,代价是构造函数是不可见的而且它的API和实现之间的界限很模糊,可能存在正确性、性能、安全性和维护方面的问题。在不使用任何小工具的情况下,你可以轻松地通过反序列化一个需要很长的反序列化时间的短流来发起拒绝服务(denial-of-service)攻击,这种流被称为反序列化炸弹(deserialization bombs )[Svoboda16]。即使您遵守所有相关的最佳实践并成功编写无法攻击的可序列化类,您的应用程序仍可能容易受到攻击。

2024-12-09 21:41:23 804

原创 第八十四条:不要依赖线程调度器

任何一个合理的操作系统在做出这样的决定时,都会努力做到公正,但是所采用的策略却大相径庭。根据Executor Framework(第80项),这意味着适当地规定了线程池的大小[Goetz06, 8.2],并且使任务保持任务简短但不会太短【尽量让执行任务的时间短一点,但不能太短】,否则调度开销会影响到性能。通过调整某些线程的优先级来改善应用程序的响应能力,这样做并非不合理,却是不必要的,也是不可移植的。保持可运行线程数量尽可能少的主要方法是,让每个线程做些有意义的工作,然后等待更多有意义的工作。

2024-12-08 21:58:25 360

原创 第八十三条:谨慎使用延迟初始化

这种习惯用法背后的思想是检查字段的值两次(因此得名 double-check):第一次不加锁定,然后,如果字段看起来没有初始化,第二次使用锁定。在某些体系结构上,它加速了字段访问,但代价是额外的初始化(每个访问字段的线程最多进行一次初始化)。如果字段只在类实例的一部分上访问,并且初始化字段的开销很大,那么延迟初始化可能是值得的。延迟初始化(像许多“优化”一样)实际上会损害性能,具体取决于这些字段中最终需要初始化的部分、初始化的开销以及初始化后每个字段被访问的频率。下面是一个通常初始化的实例字段的典型声明。

2024-12-08 21:55:40 716

原创 第八十二条:将线程安全性写在文档中

当并发使用一个类的方法时,类的行为方式是该类与其客户端建立的约定的重要组成部分。类的线程安全说明通常放在他的文档注释中,但是带有特殊线程安全属性的方法则应该在他们自己的文档注释中说明他们的属性。而且,“使用synchronized修饰符声明的存在就足以用文档说明线程的安全性”的这种说法说体现了一种误解,即线程安全是全有或全无的属性。当一个类承诺了“使用一个公有可访问的所对象”时,就意味着允许客户端以原子的方式执行一个方法调用序列,但是,这种灵活性是要付出代价的。这有可能是无意的,也可能是有意的。

2024-12-08 21:47:04 726

原创 第八十一条:与wait和notify相比,首选高级并发工具

一个相关的话题是,为了唤醒正在等待的线程,你应该使用notify还是notifyAll(回忆一下,notify唤醒的是某个正在等待的线程,假设有这样的线程存在,而notify唤醒的则是所有正在等待的线程)。这是合理而保守的建议。就好像把wait调用放在一个循环中,以避免在公有可访问对象上的意外或者恶意的通知一样,与此类似,使用notifyAll代替notify可以避免来自不相关线程的意外或者恶意的等待。最后,请注意,此示例中的代码不会产生准确的计时,除非操作执行了大量工作,例如一秒钟或更长时间。

2024-12-08 21:31:34 546

原创 第八十条:与线程相比,首选执行器、任务和流

例如,可以等待一项指定的任务【执行】完成(就项第79项,原书319页中的使用get方法一样),你可以等待一个任务集合中的任何任务或者所有任务完成(利用invokeAny或者invokeAll方法),你可以等待executor service终止(使用awaitTermination方法),你可以在完成任务时逐个检索任务结果(使用一个ExecutorCompletionService),您可以安排任务在特定时间运行或定期运行(使用一个ScheduledThreadPoolExecutor),等等。

2024-12-08 21:24:01 650

原创 第七十九条:避免过度同步

就像在第一个例子中一样,它会产生一个异常,因为调用线程已经有这个锁了,因此当该线程试图再次获得该锁时会成功,尽管概念上不相关的另一项操作正在该锁所保护的数据上进行着。notifyElementAdded方法中的迭代是在一个同步块中,可以防止并发的修改,但是无法防止迭代线程本身回调到观察的集合中,也无法防止修改它的observers列表。为了对这个过程进行更具体的说明,来考虑下面的类,它实现了一个*可以观察(observable)到的集合包装(set wrapper)。外来方法的运行时长是不固定的。

2024-12-08 21:08:11 457

原创 第七十八条:同步对并发可变数据的访问

许多程序猿把同步的概念仅仅理解为一种互斥的方式(mutual exclusion),即,当一个对象被一个线程修改的时候,可以阻止另一个线程观察到对象内部不一致的状态。换句话说,读取一个非long或double类型的变量,可以保证返回的值是某个线程保存在该变量中的,即使多个线程在没有同步的情况下并发地修改这个变量也是如此。如果没有同步,就无法保证一个线程所做的修改对另一个线程是可见的。虽然语言规范保证了线程在读取原子数据的时候,不会看到任意的数值,但是它并不保证一个线程写入的值对另一个线程将是可见的。

2024-12-08 21:01:44 440

原创 第七十七条:不要忽略异常

在某些情况下,是可以忽略异常的,例如,当关闭FileInputStream的时候忽略异常可能就是合适的。你尚未更改文件的状态,因此无需执行任何恢复操作,并且你已经从文件中读取了所需要的信息,因此没有理由中止正在进行的操作。把异常记录下来还是明智的做法,因为如果这些异常经常发生,你就可以调查异常的原因。不管异常代表了可预见的异常条件,还是编程错误,用空的catch块忽略它,将会导致程序在遇到错误的情况下悄然地执行下去。空的catch块会使异常达不到应该有的目的 ,【目的】就是强迫你处理异常的情况。

2024-12-04 20:46:23 263

原创 第七十六条:努力保持故障的原子性

一种类似的获得失败原子性的办法是,对计算过程进行排序,使得任何可能会失败的计算都在对象被修改之前发生。例如,考虑TreeMap的情形,它的元素被按照某种特定的顺序做了排序。如果一个操作失败了,它可能会阻止创建新的对象,但是永远也不会使已有的对象保持在不一致的状态之中,因为当每个对象被创建之后它就处于一致的状态之中,以后也不会再发生变化。最后一种获得失败原子性的办法远远没有那么常用,做法是编写一段恢复代码(recovery code),由它来拦截操作过程中发生的失败,以及使对象回滚到操作开始之前的状态。

2024-12-04 20:43:24 293

原创 第七十五条:将故障记录信息包含在详细信息中

实际上,这种做法可以有效地把代码几种起来放在异常类中,由这些代码对异常类自身中的异常产生高质量的详细信息,而不是要求类的每个用户都多余地产生详细信息。正如第70项中所建议的,为异常的捕获失败(failure-capture)的信息提供一些访问方法是合适的(在上述例子中的lowerBound、upperBound和index方法)。为了确保在异常的详细信息中包含足够的能够捕获失败的信息,一种办法是在异常的构造器而不是字符串详细信息中引入这些信息。然后可以自动生成包含该信息的【异常】详细信息。

2024-12-04 20:40:46 831

原创 第七十四条:将每个方法抛出的所有的异常都写在文档中

完整的文档,可以组织成为列表文档,这样就可以描述出正确使用方法的前提,让使用者一目了然。使用API的程序员必须知道哪些异常是需要受检的,哪些是不需要受检的,这很重要,因为这两种情况下他们的责任是不同的。当缺少由throws声明产生的方法标头时,由Javadoc的@throws标签所产生的文档就会提供明显的提示信息,以帮助程序员区分受检的异常和未受检的异常。每个方法,都要有文档注释,这样便于后期自己回顾当初的功能以及便于他人的阅读理解,同样的,每个方法抛出的异常,也需要文档注释。

2024-12-04 20:37:30 290

原创 第七十三条:抛出适合于当前抽象的异常

如果高层的实现在后续的发行版本中发生了变化,它所抛出的异常也可能会梗着发生变化,从而潜在地破坏现有的客户端程序。一种特殊的异常转译形式被称为*异常链(exception chaining),如果低层的异常对于调试导致高层异常的问题非常有帮助,使用异常链就很合适。大多数标准的异常都有支持链的构造器。为了避免这个问题,更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常。如果无法避免低层异常,次选方案是,让更高层来悄悄地绕开这些异常,从而将高层方法的调用者与低层的问题隔离开来。

2024-12-04 20:33:09 330

原创 第七十二条:优先使用标准的异常

虽然它们是Java平台类库中迄今为止最常被重用的异常,但是,在条件允许的情况下,其他的异常也可以被重用。】,不过,一定要确保抛出异常的条件与该异常的文档中描述的条件一致:这种重用必须建立在语义的基础上,而不是建立在名称的基础之上【就是要根据异常的语义来使用,而不要根据异常的名称来决定使用哪个异常】。其中最主要的好处是,它使你的API更加易于学习和使用,因为它与程序猿已经熟悉的习惯用法是一致的。第二个好处是,对于用到这些API的程序而言,它们的可读性会更好,因为它们不会出现很多程序猿不熟悉的异常。

2024-12-04 20:27:51 601

原创 第七十一条:避免不必要地使用检查型异常

这种重构之后的API在本质上等同于第69项中的“状态测试方法”,并且,同样的告诫依然适用:如果对象将在缺少外部同步的情况下被并发访问,或者可被外界改变状态,这种重构就是不恰当的,因为在actionPermitted和action这两个调用的时间间隔之中,对象的状态有可能会发生变化。这种重构并不总是恰当的,但是,凡是在恰当的地方,它都会使API用起来更加舒服。你也可以把受检异常变成未受检异常,这种做法就是,把这个抛出异常的方法分成两个方法,其中一个方法返回一个boolean,表明是否应该抛出异常。

2024-12-04 20:22:04 232

原创 第七十条:对于可恢复的条件,使用检查型异常;对于编程错误,使用运行时异常

例如,考虑资源耗尽的情形,这可能是因为程序中分配了不合理的大数组这样的错误而引起的,也可能是资源确实不足而引起。例如,因信用卡上的余额不足而使购物失败,从而抛出一个受检查的异常,该异常就应提供了一个方法以获得不足的金额,购物者就可以补足这个金额(从而使购物成功,程序继续运行。这种方法的主要用途是为捕获异常的代码提供额外的信息,这些信息与引起异常抛出的条件相关。因此,方法中声明要抛出的每个受检的异常,都是对API用户的一种潜在的指示:与异常相关联的条件是调用这个方法的一种可能的结果。设计者的一个判断了。

2024-12-03 21:28:54 375

原创 第六十九条:异常机制应该仅用于异常的情况

如果对象将在缺少外部同步的情况下被并发访问,或者可被外界改变状态,必须使用option或可识别的返回值,因为在调用“状态测试”方法和调用对应的“状态相关”方法的时间间隔中,对象的状态可能会发生变化。如果类具有“状态依赖(state-dependent)”的方法,即只有在特定的不可预知的条件下才可以被调用的方法,这个类往往也应该有个单独的“状态测试(state-testing)”方法,即指示是否可以调用这个状态相关的方法。然而,由这种过度聪明的模式带来的微妙的BUG,以及维护的痛苦却依然存在。

2024-12-03 21:17:16 774

原创 第六十八条:遵循普遍接受的命名惯例

返回布尔值的方法通常具有以单词is开头(很少用has开头)的名称,后跟名词,名词短语或任何用作形容词的单词或短语,例如isDigit,isProbablePrime,isEmpty,isEnabled,或hasSiblings。返回非布尔函数或调用它们的对象的属性的方法通常以名词,名词短语或以动词get开头的动词短语命名,例如size,hashCode或getTime。这个字母通常是以下五种类型之一:T表示任意的类型,E表示集合的元素类型,K和V表示映射的键和值类型,X表示异常。函数的返回类型通常是R。

2024-12-02 21:19:13 614

原创 第六十七条:谨慎进行优化

这个决定就是,这个注重性能的方法将返回Dimension实例,与此密切相关的决定是,Dimension实例是可变的,迫使这个方法的任何实现都必须为每个调用分配一个新的Dimension实例。如果好的程序不够快,它的结构将使它可以得到优化。Java的性能模型不仅定义不明确,而且在不同的JVM实现,或者不同的发行版本,以及不同的处理器,在它们这些当中也都各不相同。导致你对API进行包装的性能因素可能会在平台未来的发行版本中,或者在将来的底层软件中不复存在,但是被包装的API以及由它引起的问题将永远困扰着你。

2024-12-02 21:16:35 719

原创 第六十六条:谨慎使用本地方法

最后,本地方法可以通过本地语言,编写应用程序中注重性能的部分,从而提高系统的性能。如果你不小心,本地方法可能会降低性能,因为垃圾收集器无法自动化,甚至无法跟踪本机内存使用情况(第8项),并且在进入和退出本地代码时,需要相关的固定开销。使用本地方法来访问特定于平台的机制是合法的,但这很少是必要的(but it is seldom necessary):随着Java平台的成熟,它提供了越来越多以前只有在宿主平台上才拥有的特性。例如,在Java 9中添加的进程API,这个API提供了访问操作系统的进程的功能。

2024-12-02 21:14:30 309

原创 第六十五条:与反射相比,首选接口

而且,Constructor,Method和Field 实例使你能够通过*反射机制(reflectively)*操作它们的底层对等体:通过调用Constructor,Method和Field实例上的方法,可以构造底层类的实例、调用底层类的方法,并访问底层类中的域。这种做法就是,在所支持包所需要的最小环境下对它进行编译,通常是最老的版本,然后以反射方式访问任何更加新的类或者方法。对于有些程序,它们必须用到在编译时无法获得的类,但是在编译时存在的适当的接口或者超类,通过它们可以引用这个类(第64项)。

2024-12-02 21:12:12 914

原创 第六十四条:通过接口来引用对象

你可能会认为使用其【变量】的实现类型声明变量是可行的,因为你可以同时更改声明的类型和实现的类型,但是你无法保证这个更改可以使程序【顺利】编译。有一点值得注意:如果原来的实现提供了某种特殊的功能,而这种功能并不是这个接口的通用约定所要求的,并且代码依赖于该功能,那么新的实现也要提供同样的功能,这一点至关重要。不适合使用接口类型的最后一种情形是,类实现了接口,但是它提供了接口中不存在的额外方法————例如,PriorityQueue有一个comparator方法在当前的Wueue接口中是不存在的。

2024-12-02 20:55:12 230

原创 第六十三条:注意字符串拼接操作的性能

自Java 6以来,为了使字符串拼接更快已经做了很多工作了,但两种方法的性能差异仍然是巨大的:如果numItems返回100,并且lineForItem返回一个固定长度为80个字符的字符串,在我的机器上,第二种做法比第一种做法要块6.5倍。产生单独一行的输出,或者构造一个字符串来表示一个较小的、大小固定的对象,使用字符串拼接操作符是非常好的,但它不能缩放(but it does not scale)。例如,考虑下面的方法,它通过反复拼接每个item行,构造出一个代表statement的字符串。

2024-12-02 20:32:43 269

原创 第六十二条:如果其他类型更合适,就不要使用字符串

如果不存在这样的类型,就应该编写一个类型。要使这种方法可行,客户端提供的字符串键必须是唯一的:如果两个客户端各自决定为它们的线程局部变量使用同样的名称,它们实际上就无意中共享了这个变量,这样通常会导致两个客户端都失败。因为字符串很通用,并且Java语言也支持得很好,因此将字符串用于除设计字符串之外的其他目的的自然倾向【也就是说,设计字符串本来是用来做某些事情的,但是除了这些事情之外,人们也会使用字符串】。除了解决了基于字符串的API的问题之外,与前面的两个基于键的API相比,它还更快速、更优雅。

2024-12-02 20:29:19 727

原创 第六十一条:首选基本类型,而不是其封装类

这两个Integer实例都表示相同的值(42),因此这个表达式的值应该为0,但它输出的却是1,这表明第一个Integer的值大于第二个。在实践中,如果你需要一个比较器来描述一个类型的自然顺序,你应该简单地调用Comparator.naturalOrder(),如果你自己编写一个比较器,你应该使用比较器构造方法,或基本类型的静态比较方法(第14项)。在本项中所讨论的这三个程序中,问题是一样的:程序猿忽略了基本类型和装箱基本类型之间的区别,并承受这些后果。总之,当可以选择的时候,基本类型要优先于装箱基本类型。

2024-11-27 21:08:52 481

原创 第六十条:如果需要精确的答案,避免使用float和double

使用BigDecimal还有一些额外的好处,它允许你完全控制舍入,只要执行需要舍入的操作,就可以从八种舍入模式中进行选择。你可能会认为,只要在打印之前将结果做一下舍入就可以解决这个问题,但遗憾的是,这种做法并不总是可行的。例如,假设你的口袋有$1,你看到货架上有一排美味的糖果,标价分别为10¢, 20¢, 30¢, 等等,一直到$1。请注意,使用的是BigDecimal的String构造函数而不是它的double构造函数。如果你解决的是一个简单的问题,后一种缺点并不要紧,但是前一种缺点可能会让你很不舒服。

2024-11-27 20:54:33 307

原创 第五十九条:了解并使用类库

random方法的第三个缺点是,在极少数情况下,它的失败是灾难性的,返回一个落在指定范围之外的数。如果random方法工作正常的话,这个程序打印出来的数将接近一百万的一半,但是如果真正运行这个程序,就会发现它打印出来的数接近于666666.由random方法产生的数字有2/3落在随机数取值范围的前半部分。如果你需要做的事情看起来是十分常见的,有可能类库中已经有某个类完成了这样的工作。既然有那么多的有点,使用标准类库机制而不选择专门的实现,这显然是符合逻辑的,然而还是有相当一部分的程序猿没有这样做。

2024-11-27 20:43:53 878

原创 第五十八条:与传统的for循环相比,首选for-each循环

许多专家级的程序猿偶尔也会犯这样的错误。(Destructive filtering) ————如果需要遍历集合,并删除选定的元素,就需要使用显式的迭代器,以便一颗调用它的remove方法。如果真的那么不幸,并且外部集合的大小是内部集合大小的几倍————可能因为它们是相同的集合————循环就会正常终止,但是不会完成你想要的工作。————如果需要并行地遍历多个集合,就需要显式地控制迭代器或者索引变量,以便所有迭代器或者索引变量都可以得到同步前移(就如上述关于有问题的牌和骰子的示例中无意中所示范的那样)

2024-11-27 20:26:33 519

原创 第五十七条:最小化局部变量的作用域

如果变量的值必须在try块外部被使用到,它就必须在try块之前被声明,但是在try块之前,它还不能被“有意义地初始化”。第二个循环中包含一个“剪切-粘贴”错误:它初始化了一个新的变量i2,但是使用的是旧的那个变量i,遗憾的是,这时i仍然还在有效范围内。如果变量是在“使用它的块”之外被声明的,当程序退出该块之后,该变量仍然是可见的。无论是传统的还是for-each形式的for循环,都允许声明循环变量(loop variable),它们的作用域被限定在正好需要的范围之内。实际上,这也是很流行的做法。

2024-11-26 22:03:27 541

原创 第五十六条:为所有导出的API元素编写文档注释

同样的,也可以在一些受影响的参数的@Param标记中指定前提条件。如果API元素没有文档注释,Javadoc将会搜索最为适用的文档注释,接口的文档注释优于超类的文档注释。为了编写可维护的代码,你还应该为大多数未导出的类,接口,构造函数,方法和字段编写文档注释,尽管这些注释不需要像导出的API元素那样详细。复杂API:对于由多个相互关联的类组成的复杂API,有必要用一个外部文档来描述该API的总体结构,对文档注释进行补充,如果有这样的文档,相关的类或者包文档注释就应该包含一个对这个外部文档的链接。

2024-11-24 21:14:53 826

原创 第五十五条:谨慎返回Optional

包含自动装箱的基本类型的optional跟返回基本类型的optional相比,【返回自动装箱的基本类型】的成本是非常昂贵的,因为optional有两个级别的装箱操作,而不是零个没有【意思是optional有自己的装箱操作?使用流进行编程时,发现自己使用Stream并要求包含非空选项中的所有元素的Stream可以继续执行【代码】【也就是说当你要求返回的所有Stream里面,如果Optional里面是有值的,那就继续执行代码,没有值的话就忽略】,这种情况是很常见的。

2024-11-24 20:13:18 759

原创 第五十四条:返回空的集合或数组,而不是null

通常,应返回空的容器或数组,而不是null。即使在性能考虑下,返回空集合或数组也不涉及额外开销,因为可以复用不可变的空实例。对于数组,同样应返回零长度的数组,而不是null,以避免性能损耗和错误。优化时,可以重复返回相同的空数组,但应先验证是否真正需要这种优化,以免适得其反。其次对于不返回任何元素的调用,每次都返回同一个零长度数组是可能的,因为零长度数组是不可变的,而不可变对象有可能被自由共享。正常情况下,toArray方法分配了返回的数组,如果集合是空的,它将使用零长度输入数组。

2024-11-21 21:37:02 323

原创 第五十三条:谨慎使用可变参数

其中最严重的问题是,如果客户端调用这个方法时,并没有传递参数进去,它就会在运行时失败,而不是在编译时失败,另一个问题是,这段代码很不美观。可变参数机制通过先创建一个数组,数组的大小为在调用位置所传递的参数数量,然后将参数值传递到数组中,最后将数组传递给方法。声明该方法带有两个参数,一个是指定类型的正常参数,另一个是这种类型的可变参数。例如,下面就是一个可变参数方法,带有int参数的一个序列,并返回它们的总合。首先要说明一下,在现实项目中很少有人会写可变参数的方法,一般来说,源码上用可变参数的情况要多得多。

2024-11-21 21:24:48 402

原创 第五十二条:谨慎使用重载

在CollectionClassifier这个示例中,该程序的意图是:根据参数的运行时类型自动将调用分发给适当的重载方法,以此来识别出参数的类型,就好像Wine的例子中的name方法所做的那样。从技术上讲,问题是System.out :: println是一个不精确的方法引用[JLS,15.13.1],并且“包含隐式类型的lambda表达式或不精确的方法引用的某些参数表达式被适用性测试忽略,因为它们的在选择目标类型之前无法确定含义[JLS,15.12.2]。>,在循环的每次迭代中,都会调用这个重载方法。

2024-11-21 21:18:13 1002

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除