要使局部变量的作用域最小化, 最有力的方法就是在第一次使用它的地方声明.
几乎每个局部变量的声明都应该包含一个初始化表达式. (例外: try-catch).
for循环允许声明循环变量, 其作用域被限定在正好需要的范围之内. -> 优于while循环.
方法应该小而集中.
for-each循环(增强型for循环)在简洁性和预防Bug方面有着传统for循环无法比拟的优势, 并且没有性能损失, 应该尽可能地使用for-each循环.
增强型for循环中的:
读作in
.
但是有三种情况无法使用for-each循环:
- 过滤删除.
- 转换更新.
- 平行迭代. 多个集合的同步位移.
for-each循环可以用在任何实现了Iterable
接口的对象上.
举例: 随机数的例子 -> 了解和使用类库. (Java 7不使用Random
, 而用ThreadLocalRandom
. 另, 还有SplittableRandom
.)
不要重新发明轮子.
好处:
- 充分利用专家知识和前人经验.
- 时间.
- 类库的性能会不断提高.
- 类库功能会扩展.
- 使代码融入主流, 易读易维护.
关注类库更新新加入的功能. 尤其是java.lang
, java.util
和java.io
包下的.
还有collections, streams, concurrent等.
货币计算不能用float
或double
, 应该用BigDecimal
, int
或者long
.
BigDecimal
没有原生类型使用起来方便, 而且会有性能影响. 优点是可以自己选择舍入模式.
int
(9位)或long
(18位)需要自己处理小数点移位.
基本类型和装箱基本类型的三个主要区别:
- 基本类型只有值, 而装箱基本类型则具有与它们的值不同的同一性.
- 基本类型只有功能完备的值, 而装箱基本类型还有非功能值null.
- 基本类型通常比装箱基本类型更节省时间和空间.
有问题的情形:
- 对装箱基本类型运用
==
操作符进行比较, 几乎总是错误的. - 当一项操作中混合装箱基本类型和基本类型时, 会自动拆箱, 如果null被自动拆箱会抛出
NullPointerException
. - 变量被反复自动装箱和拆箱, 会有性能问题.
装箱基本类型的合理用处:
- 作为集合中的元素, 键和值.
- 在参数化类型中必须使用装箱基本类型.
- 在进行反射的方法调用时必须使用装箱基本类型.
字符串不适合代替其他的值类型. -> int
, float
, BigInteger
, boolean
等.
字符串不适合代替枚举类型, 聚集类型, 也不适合代替能力表(capabilities).
总而言之, 如果可以使用更加合适的数据类型, 或者可以编写更加适当的数据类型, 就应该避免用字符串来表示对象. 若使用不当, 字符串会比其他的类型更加笨拙, 更不灵活, 速度更慢, 也更容易出错.
为连接n个字符串而重复地使用字符串连接操作符(+
), 需要n的平方级的时间.
这是由于字符串的不可变而导致的. 当两个字符串被连接在一起时, 它们的内容都需要被拷贝.
连接多个项目, 为了性能, 请使用StringBuilder
的append()
.
如果有合适的接口类型存在, 那么对于参数, 返回值, 变量和域来说, 就都应该使用接口类型进行声明.
这样做程序将会更加灵活 -> 当你决定更换实现的时候, 值需要改变调用构造器的那句.
如果没有适当的接口, 则使用类层次结构中提供了必要功能的最基础的类.
反射机制提供了"通过程序来访问关于已装载的类的信息"的能力.
这种能力的代价:
- 丧失了编译时类型检查的好处.
- 执行反射访问所需要的代码非常笨拙和冗长.
- 性能损失.
也有一些情形, 通过以非常有限的形式利用, 你可以获得反射的好处, 而不被它的cost影响: 如果你编写的程序必须要与编译时未知的类一起工作, 如有可能, 就应该仅仅使用反射机制来实例化对象, 而访问对象时则使用编译时已知的某个接口或者超类.
Java Native Interface (JNI)允许Java应用程序可以调用本地方法(native method), 即本地程序设计语言(C或者C++)来编写的特殊方法.
使用本地方法来提高性能的做法不值得提倡, 因为JVM实现变得越来越快了.
使用本地方法有一些缺点: 不安全; 与平台相关, 不可自由移植; 更难调试; 进入和退出本地代码时需要相关的固定开销; 需要胶合代码的本地方法编写起来单调乏味, 且难以阅读.
不要因为性能而牺牲合理的结构.
努力避免那些限制性能的设计决策.
要考虑API设计决策的性能后果. 为了性能而包装API -> bad idea.
在每次试图做优化之前和之后, 要对性能进行测量.
总而言之, 不要费力去编写快速的程序, 应该努力编写好的程序, 速度自然会随之而来.
在设计系统的时候, 特别是在设计API, 线路层协议和永久数据格式的时候, 一定要考虑性能的因素.
当构建完成之后, 要测量它的性能. 如果不够快, 可以在性能剖析器的帮助下, 找到问题的根源, 然后设法优化系统中相关的部分. 第一个步骤是检查所选择的算法: 再多的底层优化也无法弥补算法的选择不当. 必要时重复这个过程, 在每次改变之后都要测量性能, 直到满意为止.
Java平台建立了一整套很好的命名惯例(naming convention).
- 包/模块名: 层次状, 小写字母或数字(很少使用数字),
.
分隔. - 类, 接口: 一个或多个单词, 首字母大写.
- 方法和域, 局部变量: 首字母小写.
- 常量域: 一个或多个大写的单词, 下划线分隔.
- 类型参数: 单个字母: T表示任意的类型, E表示集合元素类型, K和V表示映射的键和值, X表示异常. 任何类型的序列可以是T, U, V或者T1, T2, T3.
一些语法惯例:
- 可实例化的类通常用单数名词, 不可实例化的辅助类通常用复数名词, 如
Collections
. - 方法名通常是动词或动词短语.
- 返回布尔值的方法通常以
is
或has
开头. - 方法返回非布尔值时, 有时用名词命名, 如
size
, 有时加get
. - 转换类型的方法通常用
toType
. - 返回不同视图的方法用
asType
. - 还有
typeValue
和静态工厂方法等.