重读 Effective JAVA(二)- 精进自己的 JAVA 技术
将局部变量的作用域最小化
使一个局部变量的作用域最小化,最有力的技术是在第一次使用它的地方声明
几乎每一个局部变量的声明都应该包含一个初始化表达式
个人理解:局部变量要用的地方用即可,尽量取一个易懂的名字,如果跨方法使用最好定义一个全局变量
了解和使用库
不要从头发明轮子!!
个人理解:自己写的一般都不会比现成的好,了解和使用更多库,可以让你的代码简便并更具安全性
如果要求精确的答案,避免使用 float 和 double
float 和 double 主要设计目标是为了科学计算和工程计算,最好的做法是使用 BigDecimal,或者使用 int 或者 long 类型,自己处理小数
个人理解:一般就是 BigDecimal 就够用了
了解字符串连接的性能
连接操作符(+)最便捷,但是不适合规模比较大的情形,为连接 n 个字符串而重复地使用字符串连接符,要求 n 的平方级时间
为了获得可接受的性能,使用 StringBuffer 替代 String
个人理解:只要明白 String,StringBuffer,StringBuilder 的区别,各自的优势,使用场景即可,
不可变性:
String
类是不可变的,每次进行字符串拼接,切割等操作都会产生新的字符串,旧的字符串便会成为垃圾等待回收。StringBuffer
类和StringBuilder
类是可变的。
线程安全:
StringBuffer
是线程安全的,方法大多是同步方法(synchronized),所以在多线程情况下,建议使用StringBuffer
。StringBuilder
是非线程安全的。
效率: 由于
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,接口也应该很少会扩展它,但是可以考虑提供一个无参数的构造函数
版权声明: 本文为 InfoQ 作者【xfgg】的原创文章。
原文链接:【http://xie.infoq.cn/article/9bc265dd7232edb601704a297】。未经作者许可,禁止转载。
评论