第45条 将局部变量的作用域最小化
- 要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方进行声明。
如果变量在使用之前很早进行声明,对于阅读代码的人来说,只会分散注意力。看到使用变量的地方,早已忘记当初是怎么声明的了。还需要返回去寻找最初声明的地方。 - 每个局部变量的声明都应该包含一个初始化表达式。如果当前还不能进行有意义的初始化,就应该推迟声明,直到可以初始化为止。
- 循环中提供了特殊的机会来将变量的作用域最小化。for循环允许声明循环变量(loop variable),它们的作用域正好被限定在需要的循环范围之内。因此,如果在循环终止之衙不再需要循环变量了,for循环就优先于while循环。
- 最后一种方法是 使用方法小而集中。如果把两个操作合并到同一个方法,与其中一个操作相关的局部变量就出现在另一个操作的代码范围之内。为了防止这种情况发生,只要把这个方法分成两个,每个方法各执行一个操作就好了。
第46条 foreach循环在读取元素上优于传统的for循环
for-each循环不仅可以遍历集合和数组,还让你遍历任何实现Iterable接口的对象。此接口与for-each循环同时被增加到Java 1.5平台中。
实现Iterable接口很简单,如果正在设计一个类表示的是一组元素,需要对外提供遍历的功能,最好实现Iterable接口,以便可以使用for-each循环。
for-each循环在简洁性和预防bug方面有着传统for循环无法比拟的优势,并且没有性能损失,所以应尽可能使用for-each循环。但有三种情况无法使用:过滤集合、转换元素、平行迭代多个集合
第47条 了解和使用类库
使用标准类库的好处:
- 通过使用标准类库,可以充分利用这些编写标准类库的专家的知识,以及在你之前的其他人的使用经验。
- 不必浪费时间为那些与工作不太相关的问题提供特别的解决方案。就像大多数程序员一样,应该把时间花在如何实现具体的业务功能上。
- 使用类库中的方法,它们的性能会随着时间的推移而不断提高,无需你做任何努力。因为很多人在使用它们,它它被当作工业标准在使用,所有类库的维护组织有强烈的动机想使它们运行得更快。
- 可以使自己的代码融入主流。这样的代码更易读、更易维护、更易被大多数的开发人员重用。
因此众多好处,在每个重要的发行版本中,都会有许多新的特性被加入到类库中,所以与这些新我保持同步是值得的。由于类库太庞大,不可能去学习所有的文档。
但是基本的java.lang、java.util、 java.io,这三个包里的类应该多熟悉一些。
另外 java.util.concurrent 也是值得研究的。
总而言之,不要重新发明轮子。如果要做的事情看起来是十分常见的,有可能类库中已经有某个类完成了这样的工作,使用工业级代码比自己实现的可能会更加健壮、性能更好。
第48条 如果需要精确答案,不要使用float和double
float和double类型主要是为了科学计算和工程计算而设计的。它们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。
尤其是涉及到货币业务,一定不能用float和double。
BigDecimal 可以记录小数点,可以完全控制舍入操作,支持8种舍入模式。写法稍微麻烦,性能一般。
int long,需要自己选定合适的计算单位,自己记录小数点,性能比BigDecimal高。
数值范围≤9位十进制数,可以使用int;≤18位数字,可以使用long;如果超过18位,必须用BigDecimal。
第49条 基本类型优先于装箱基本类型
基本类型(primitive)int double boolean
引用类型(reference type)String List
装箱基本类型(boxed primitive)Integer Double Boolean
同一性比较(identity comparistion)
区别:
- 基本类型侧重于值,装箱基本类型侧重于对象。所以装箱基本类型可以为null
- 装箱基本类型更占空间,使用时效率也没有基本类型高
注意:当在一项操作中混合使用基本类型与装箱基本类型时,装箱基本类型就一定会自动拆箱。
- 混合使用时,要注意避免无意识的大量拆箱与装箱操作,否则会大大降低程序的性能。
- 如果装箱基本类型对象是null,拆箱就会发生NullPointerException异常。
装箱基本类型的使用场合:
- 做为集合中的元素、键、值
- 做为参数化类型的 类型参数,如:List
- 在进行反射的方法调用时,必须使用装箱基本类型。
总之,基本类型要优于装箱类型,它更简单,更快速。
第50条 尽量避免使用字符串
- 字符串不适合代替其他的值类型,可转为int、float、BigInteger、boolean或编写一个类。
- 不适合代替枚举类型
- 不适合代替聚集类型
- 不适合代替能力表(Capabilities)
第51条 当心字符串连接的性能
使用字符串连接操作符(+,string concatenation operator)可以方便地把多个字符串合并为一个。但是它不适合在大规模的场景中使用。
如果要拼接的字符串数量巨大,此方法的执行时间将难以估算。为了获得可以接受的性能,请使用StringBuilder替代String连接操作符。Java1.5 中增加了非同步的StringBuilder类,代替已过时的StringBuffer
第52条 通过接口引用对象
应该优先使用接口而不是类来引用对象。如果有合适的接口类型存在,那么对于参数、返回值、变量和域来说,就都应该使用接口类型进行声明。
养成用接口作为类型的习惯,使你的程序更加灵活。
使用接口做为类型,将来可以替换为新的实现,以提供更好的性能和额外的功能。
如果没有合适的接口存在:
对象属于一个框架,框架的基本类型是类,不是接口。基于类的框架(class-based framework),应该使用相关的基类(base class,往往是抽象类)来引用这个对象,而不是用它的实现类。
实际上,给定的对象是否具有合适的接口是很明显的。如果是,就用接口引用对象,会使程序更加灵活;如果不是,则使用类层次结构中提供了必要功能的最基础的类。
第53条 接口优先于反射机制
核心反射机制提供了“通过程序来访问关于已装载的类的信息”。Constructor、Method和Field实例使你能够通过反射机制操作它们的底层对等体,可以构造底层类的实例、调用底层类的方法,并访问底层类中的域。
使用反射的代价:
- 丧失了编译时类型检查的好处
- 执行反射访问所需要的代码非常笨拙和冗长
- 性能损失
普通应用程序在运行时不应该以反射方式访问对象。有一些复杂的程序需要使用反射机制。如类浏览器、对象监视器、代码分析工具、解释型的内嵌式系统、RPC系统。
如果只是以非常有限的形式使用反射机制,虽然也要付出少许代价,但是可以获得许多好处。
如果必须用到在编译时无法获取的类,但是在编译时存在适当的接口或是超类,通过它们可以引用这个类。此时,就可以以反射的方式创建实例,然后通过它们的接口或者超类,以正常的方式访问这些实例。甚至根本不需要使用java.lang.reflect包;Class.newInstance方法就已经提供了所需的功能。
第54条 谨慎地使用本地方法
Java Native Interface(JNI)允许Java应用程序调用本地方法(native method),所谓的本地方法是指本地程序设计语言如C或C++ 来编写特殊方法。本地方法在本地语言中执行任意的计算任务,并返回到Java程序设计语言。
本地方法有三个用处:
- 可以访问特定于平台的机制
- 访问遗留代码
- 提高性能(不提倡以此为目的)
Java1.4的java.util.prefs提供了注册表的功能;Java1.6的java.awt.SystemTray可以访问桌面系统托盘区。
使用本地方法来提高性能的做法不值得提倡。JVM的实现越来越快了。
本地方法的缺点:
- 本地语言是不安全的
- 不可再自由移植
- 难以调试
- 与Java语言桥接的 胶合代码,编写单调乏味,难以阅读
第55条 谨慎地进行优化
要努力编写好的程序而不是仅仅是快的程序。好的程序结构可以实现信息隐藏,更好的灵活性。
系统结构很重要,第一次设计的结构与实现的代码决定了程序的质量等级。如果已经实现成型了,再想修改结构是一件非常难的事,一般需要全部推倒重来。不要妄想先实现功能,最后再修改实现以提高程序的性能。在设计阶段就要考虑到性能问题。
努力避免那些限制性能的设计方案。尤其需要注意的是:
模块之间交互的地方,如API、线路层协议、永久数据格式。这些组件不仅在事后难以甚至不可能改变,而且有可能对系统的性能产生严重的限制。
一般来说,好的API设计也会带来好的性能。
如果产生了一个清晰、简明、结构良好的实现,此时若对性能不满意,就可以考虑性能优化。在每次优化前和优化后,要对性能进行测量。借助性能剖析工具,找到性能瓶颈。算法的选择对性能影响非常大。
第56条 遵守普遍接受的命名惯例
包名:应该比较简短,通常不超过8个字符。鼓励使用有意义的缩写形式,如util而不是utilities。只取首字线的缩写形式也是可以接受的,如awt。一个单词或一个缩写。
类型参数名称,通常约定:
T表示任意的类型,E表示集合的元素类型,K和V表示映射的键和值,X表示异常。
任何类型的序列可以是T、U、V或T1、T2、T3
转换类型的方法,通常写成toType,如toString,toArray
静态工厂方法名,常用的valueOf,getInstance,newInstance