《Effective Java》-通用编程

65 阅读6分钟

通用编程

Item 57: Mminimize the scope of local variables

最小化局部变量的作用域

最小化局部变量的作用域,可以增强代码的可读性与可维护性,降低出错的概率。

实现该目的的最有效的途径就是,在第一次使用到该局部变量的地方进行声明

此外,在声明局部变量的时候,最好对其进行赋值。不过,如果该变量在进行初始化的时候可能会抛出一个受检异常,那么该变量的声明在try-catch语句外之外,其初始化在try-catch语句块之内。

第三种有效的方式是在循环遍历过程中,能用for循环就不用while循环,因为while循环的控制参数的作用范围更大,能用for each循环就不用for循环。

最后一种情况就是,让方法变得小而集中。这句话有两层含义,第一点就是方法的行数要尽量少,第二点就是方法的功能单一。如果方法具有复杂的功能或者复杂的操作,那么不同功能、操作之间就有相互影响的可能。

Item 58: Prefer for-each loops to traditional for loops

for-each循环优先于传统的for循环

使用传统的for循环有什么缺点呢?

在多数情况下,我们只是关注容器中的元素。不管是迭代器遍历还是下标索引遍历,他们都在遍历的过程中引入了混乱因子:迭代器或者索引。在遍历的过程中,我们不仅要关注需要的元素,还要分出精力来注意迭代器或者索引是否正确。

增强for each循环有效解决了上述问题,它可以让你的关注点集中在容器中的元素本身。

但是,谨记下面的情况,不能使用for each循环:

  • 遍历集合,并删除符合条件的元素。使用for-each循环会抛出并发修改CME的异常。
  • 转换:遍历集合,并将其部分元素或者全部元素转换为指定的元素
  • 平行迭代:并行遍历多个集合

Item 59: Know and use the libraries

了解并使用类库

举个例子,如果想要编写一个随机数生成程序,有必要去深入研究同余伪随机生成器、数论、2的求补算法等理论知识。

幸运的是,Java 7开始提供了非常强大的伪随机生成器ThreadLocalRandom,它能产生更高质量的随机数,并且速度非常快。

总之,使用并了解类库具有诸多的好处,除非你热衷于造轮子。

对于每个Java开发程序员,都应该熟练掌握的类库有:

  • java.lang | java.util | java.io及其子包中的内容
  • Collections Framework和Stream类库

Item 60: Avoid float and double if exact answers are required

如果需要更加精确的结果,避免使用floatdouble

floatdouble类型尤其不适合用于货币计算!!!

用于货币计算,最好使用BigDecimalint或者long类型。

BigDecimal具有两个明显的缺点:

  • 使用不方便
  • 运行速度比较慢

使用int | long可以解决BigDecimal的问题,使用分作为金额单位,但是需要自己处理十进制小数点。

Item 61: Prefer primitive types to boxed primitives

基本数据类型优先包装类

基本数据类型和包装类型的主要区别如下:

  • 基本类型直接存储在栈内存中,访问速度快,而包装类存储在堆内存中
  • 基本数据类型不是对象,没有方法调用,包装类提供了许多有用的方法
  • 基本数据类型比包装类更加节省空间和时间

不要对包装类使用==操作符,会出现意外的错误。此外,如果在一项操作中混合使用基本类型和包装类型,包装类型会自动拆箱,如果是null引用则会抛出NPE的异常。

可以使用包装类的情况:

  • 作为集合中的元素
  • 进行泛型编程
  • 进行反射方法的调用

Item 62: Avoid strings where other types are more appropriate

当其他类型更加合适的时候,避免使用字符串类型

字符串不适合替代其他值的类型。 一般通过网络或者文件获取到的数据都是字符串类型的,当拿到这些数据之后,首先要将其转换为合适的数据类型,而非字符串。

Item 63: Beware the performance of string concatenation

了解字符串连接的性能

字符串连接操作符(+)为多个字符串拼接提供了遍历操作。但是,当进行大规模的字符串拼接的时候,该操作符的性能会大大恶化。

为了性能考虑,使用StringBuilder来替代+操作符,实际上,+也是通过StringBuilder来实现的,但是会创建大量的的对象。

Item 64: Refer to objects by their references

通过接口引用对象

如果有合适的接口类型存在,对于参数、返回值、变量和字段来说,都应该使用接口类型进行声明。

当然,这条准则并不是一贯成立的。如果某个类提供了接口中不存在的方法,并且程序中需要这些额外的方法,这种情况就应该引用其具体实现。

Item 65: Prefer interfaces to reflection

接口优先于反射

反射机制提供了通过程序访问任意类的能力,通过一个类的Class对象,可以获取该类的构造器、方法以及字段。

反射机制具有如下的缺点:

  • 损失了编译时检查的能力,包括异常检查
  • 反射代码冗长笨拙
  • 反射方法的执行效率相较于普通方法慢很多

不过,如果合理利用反射机制,也可以获取不错的效果。例如,对于一些编译时期不可用的类,可以通过反射机制创建类的实例,然后通过他们的接口或者超类访问这些实例。

Item 66: Use native methods judiciously

谨慎地使用本地方法

所谓本地方法(Naive methods)是指使用本地编程语言来编写的方法。其提供了诸如访问注册表、遗留数据等能力。此外,使用本地方法也可以提升程序的运行速度。

但是,不提倡调用本地方法来提供性能。

  • 本地语言是不安全的
  • 本地语言是平台相关的
  • 垃圾回收器无法追踪本地内存

Item 67: Optimize judiciously

谨慎地进行优化

不要优化!不要优化!!不要优化!!!

优化弊大于利,特别是不成熟的优化。

我们应该把精力放在如何编写高质量的代码,而不是如何进行优化。

Item 68: Adhere to generally accepted naming conventions

遵守普遍接受的命名惯例

  • 类、接口、枚举:首字母大写,避免使用缩写
  • 方法和字段:首字母小写,驼峰命名
  • 常量:大写,中间使用下划线进行分割
  • 类型参数:
    • T:任意类型
    • E:元素类型
    • K | V:键值类型
    • X:异常类型

将标准的命名规范当做一种内在的机制看待,并学着用他们作为第二特性。