写点什么

重读 Effective JAVA(二)- 精进自己的 JAVA 技术

作者:xfgg
  • 2023-10-25
    福建
  • 本文字数:2127 字

    阅读完需:约 7 分钟

重读 Effective JAVA(二)- 精进自己的 JAVA 技术

将局部变量的作用域最小化

使一个局部变量的作用域最小化,最有力的技术是在第一次使用它的地方声明

几乎每一个局部变量的声明都应该包含一个初始化表达式

个人理解:局部变量要用的地方用即可,尽量取一个易懂的名字,如果跨方法使用最好定义一个全局变量

了解和使用库

不要从头发明轮子!!

个人理解:自己写的一般都不会比现成的好,了解和使用更多库,可以让你的代码简便并更具安全性

如果要求精确的答案,避免使用 float 和 double

float 和 double 主要设计目标是为了科学计算和工程计算,最好的做法是使用 BigDecimal,或者使用 int 或者 long 类型,自己处理小数

个人理解:一般就是 BigDecimal 就够用了

了解字符串连接的性能

连接操作符(+)最便捷,但是不适合规模比较大的情形,为连接 n 个字符串而重复地使用字符串连接符,要求 n 的平方级时间

为了获得可接受的性能,使用 StringBuffer 替代 String

个人理解:只要明白 String,StringBuffer,StringBuilder 的区别,各自的优势,使用场景即可,

  1. 不可变性

  • String类是不可变的,每次进行字符串拼接,切割等操作都会产生新的字符串,旧的字符串便会成为垃圾等待回收。

  • StringBuffer类和StringBuilder类是可变的。

  1. 线程安全

  • StringBuffer是线程安全的,方法大多是同步方法(synchronized),所以在多线程情况下,建议使用StringBuffer

  • StringBuilder是非线程安全的。

  1. 效率: 由于StringBuilder相对StringBuffer无需处理线程安全问题,所以StringBuilder的效率通常高于StringBuffer

适用场景: 根据以上特性,我们可以得出以下的判断:

  • 如果需要操作的字符串较少,可以直接使用String

  • 对于单线程下的大量字符串操作,建议使用StringBuilder

  • 对于多线程下的大量字符串操作,建议使用StringBuffer

具体的场景具体分析

通过接口引用对象

用接口作为类型的习惯,那么程序将会更加的灵活

但是如果没有合适的接口存在的话,那么,用类而不是接口来引用一个对象,是完全合适的

个人理解:这个前提是对象之间有共有的方法,vo,dto,entity 就不适用,还是具体情况具体分析比较好

接口优先于映像机制

映像提供了通过程序来访问关于已装载的类的信息的能力,但是这种能力需要付出代价

  • 损失了编译时类型检查的好处

  • 要求执行映像访问的代码非常笨拙和冗长

  • 性能损失

可以用映像的方式创建实例,然后通过它们的接口或者超类,以正常方式访问这些实例

个人理解:通过映像接口的方式比较好,个人用的比较少

谨慎的进行优化

努力编写好的程序而不是快的程序

努力避免限制性能的设计决定

考虑你的 API 设计决定的性能后果

对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常

Java 提供了三种可抛出结构:被检查的异常(checked exception),运行时异常(run-time exception)和错误(error)

个人理解:通过抛出不同的异常来提示不同的信息,但个人一般都是自定义全局异常,然后捕捉后提供自己想要的信息

避免不必要地使用被检查的异常

个人理解:被检查的异常一般都可以通过代码来规避

努力使失败保持原子性

一个失败的方法调用应该使对象保持它在被调用之前的状态,具有这种属性的方法被称为具有失败原子性

最简单的方法就是设计一个非可变的对象

不要忽略异常

空的 catch 块会使异常达不到应有的目的,至少应该包含一条说明,用来解释为什么忽略掉这个异常

对共享可变数据的同步访问

synchronized 关键字可以保证在同一时刻,只有一个线程在执行一条语句,或者一段代码块

读写原子数据时,为了在线程之间可靠地通信,以及为了互斥访问,同步是需要的

无论何时当多个线程共享可变数据的时候,每个读或者写数据的线程必须获得一把锁

个人理解:这段这本书写的并不是很好,后续读一下 java 并发编程相关的书籍

避免过多的同步

为了避免死锁的危险,在一个被同步的方法或者代码块中,永远不要放弃对客户的控制

在同步区域内应该做尽量少的工作

永远不要在循环的外面调用 wait

总是使用 wait 循环模式来调用 wait 方法,永远不要在循环外调用 wait!!!

不要依赖于线程调度器

任何依赖于线程调度器而达到正确性或性能要求的程序,很有可能是不可移植的

不要企图通过调用 Thread.yield 来修正程序

个人理解:Thread.yield() 方法是一种启发式的、对调度器的一个建议,表明当前线程更愿意让出 CPU 的使用。调度器可以无视这个建议。当调度器愿意接受这个建议时,当前线程会让出 CPU 使用权,让别的线程得以运行。

具体来说,它可能导致线程从执行状态(Running)回到可执行状态(Runnable),以使得其他同优先级的线程得以运行,这也被称为线程让步。但是需要注意的是,这种让步可能无法生效,因为线程调度行为依赖于系统的线程调度策略和可用的硬件,可能有可能当前的线程再次被选中执行。

因此,Thread.yield 方法通常在调试和测试中使用,它可以帮助找出在并发条件下的 bug。然而在实际开发中它不常使用,因为它无法确保达到预期的行为,如让出 CPU 使用权。

谨慎地实现 Serializable

实现序列化直接开销很低,但是长期开销往往是实实在在的

实现 Serializable 付出最大的代价是一旦一个类被发布,则改变这个类的实现的灵活性将大大降低,同时增加了错误和安全漏洞的可能性,随着一个类的新版本的发行,相关的测试负担增加

为了继承而设计的类应该很少实现 Serializable,接口也应该很少会扩展它,但是可以考虑提供一个无参数的构造函数

发布于: 刚刚阅读数: 4
用户头像

xfgg

关注

THINK TWICE! CODE ONCE! 2022-11-03 加入

目前:全栈工程师(前端+后端+大数据) 目标:架构师

评论

发布
暂无评论
重读 Effective JAVA(二)- 精进自己的 JAVA 技术_Java_xfgg_InfoQ写作社区