美文网首页
《Effective Java》总结

《Effective Java》总结

作者: 今有所思 | 来源:发表于2017-08-02 10:55 被阅读38次

2 创建和销毁对象

item 1 考虑用静态工厂方法代替构造器

优点

  1. 有名称
  2. 不必在每次调用它们的时候都创建一个新对象
  3. 可以返回原返回类型的任何子类型的对象
  4. 在创建参数化类型实例的时候,它们使代码变得更加简洁(Java 7 已经实现)

缺点

  1. 类如果不含有公有的或者受保护的构造器,就不能被子类化
  2. 它们与其他的静态方法实际上没有任何区别

item 2 遇到多个构造器参数时要考虑用构建器(Builder)

如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候。

item 3 用私有构造器或者枚举类型强化Singleton属性

注意的问题:反射和序列化
序列化:为了维护并保证Singleton,必须声明所有实例域都是transient(瞬时)的,并提供一个readResolve方法,否则,每次反序列化一个序列化的实例时,都会创建一个新的实例。

单元素的枚举类型已经成为实现Singleton的最佳方法

item 4 通过私有构造器强化不可实例化的能力

item 5 避免创建不必要的对象

当你应该重用现有对象的时候,请不要创建新的对象

item 6 消除过期的对象引用

内存泄漏(无意识的对象保存)

  1. 类自己管理内存
  2. 缓存
  3. 监听器和其他回调

item 7 避免使用终结方法

除非是作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。

3 对于所有对象都通用的方法

item 8 覆盖equals时请遵守通用约定

等价关系

  1. 自反性
  2. 对称性
  3. 传递性
  4. 一致性
  5. 非空性

item 9 覆盖equals时总要覆盖hashcode

原因:相等的对象必须具有相等的散列码

尽力做到:不相等的对象有不相等的散列码

item 10 始终要覆盖toString

item 11 谨慎地覆盖clone

  • 覆盖clone方法要非常小心,如果类里面含有复杂数据类型,要进行深度复制,如果类里面有final属性,则无法进行clone,因为final属性在clone时无法再进行赋值。

  • 拷贝构造器或者拷贝工厂,唯一参数类型是包含该构造器的类

item 12 考虑实现Comparable接口

public interface Comparable<T> {
    int compare(T t);
}

将这个对象与指定的对象进行比较。当该对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或者正整数。

4 类和接口

item 13 使类和成员的可访问性最小化

除了公有静态final域的特殊情形之外,公有类都不应该包含公有域。并且要确保公有静态final域所引用的对象都是不可变的

item 14 在公有类中使用访问方法而非公有域

item 15 使可变性最小化

不可变的类:

  1. 不要提供任何会修改对象状态的方法
  2. 保证类不会被扩展
  3. 使所有的域都是final的
  4. 使所有的域都成为私有的
  5. 确保对于任何可变组件的互斥访问

优点:

  1. 不可变对象比较简单
  2. 不可变对象本质是线程安全的,它们不要求同步。不可变对象可以被自由地共享。
  3. 不可变对象为其他对象提供了大量的构件(不可变对象构成了大量的映射键和集合元素)

缺点:
对于每个不同的值都需要一个单独的对象。

item 16 复合优先于继承

继承的功能非常强大,但是也存在诸多问题,因为它违背了封装原则。只有当子类和超类之间确实存在子类型关系时,使用继承才恰当。即便如此,如果子类和超类处在不同的包中,并且超类并不是为了继承而设计的,那么继承将会导致脆弱性。

item 17 要么为继承而设计,并提供文档说明,要么就禁止继承

item 18 接口优于抽象类

接口通常是定义允许多个实现的类型的最佳途径。

item 19 接口只用于定义类型

接口应该只被用来定义类型,它们不应该被用来导出常量。

item 20 类层次优于标签类

标签类很少有适用的时候。当你想要编写一个包含显示标签域的类时,应该考虑一下,这个标签是否可以被取消,这个类是否可以用类层次带代替。当你遇到一个包含标签域的现有类时,就要考虑将它重构到一个层次结构中去。

item 21 用函数对象表示策略

函数指针的主要用途就是实现策略模式。为了在Java中实现这种模式,要声明一个接口来表示该策略,并且为每个具体策略声明一个实现了该接口的类。

item 22 优先考虑静态成员类

嵌套类是指被定义在另一个类的内部的类,嵌套类存在的目的应该只是为它的外围类服务。
嵌套类有4种:

  1. 静态成员类 作为公有的辅助类
  2. 非静态成员类 定义一个Adapter
  3. 匿名类 创建函数对象 创建过程对象 在静态工厂方法的内部
  4. 局部类 在任何可以声明局部变量的地方,都可以声明局部类,并且局部类也遵守同样的作用域规则

后三种都被称为内部类。

5 泛型

item 23 请不要在新代码中使用原生态类型

item 24 消除非受检警告

item 25 列表优先于数组

item 26 优先考虑泛型

item 27 优先考虑泛型方法

item 28 利用有限制通配符来提升API的灵活性

item 29 优先考虑类型安全的异构容器

6 枚举和注解

item 30 用enum代替int常量

item 31 用实例域代替序数

item 32 用EnumSet代替位域

item 33 用EnumMap代替序数索引

item 34 用接口模拟可伸缩的枚举

item 35 注解优先于命名模式

item 36 坚持使用Override注解

item 37 用标记接口定义类型

7 方法

item 38 检查参数的有效性

每当编写方法或者构造器的时候,应该考虑它的参数有哪些限制。应该把这些限制写到文档中,并且在这个方法体的开头处,通过显示的检查来实施这些限制。

item 39 必要时进行保护性拷贝

如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性地拷贝这些组件。

item 40 谨慎设计方法签名

  1. 谨慎地选择方法的名词
  2. 不要过于追求便利的方法
  3. 避免过长的参数列表
  4. 对于参数类型,要优先使用接口而不是类
  5. 对于boolean参数,要优先使用两个元素的枚举类型

item 41 慎用重载

要调用哪个重载方法是在编译时做出决定的。

对于重载方法的选择是静态的,而对于被覆盖的方法的选择是动态的。

item 42 慎用可变参数

在定义参数数目不定的方法时,可变参数方法是一种很方便的方式,但是它们不应该被过度滥用。如果使用不当,会产生混乱的效果

item 43 返回零长度的数组或者集合,而不是null

item 44 为所有导出的API元素编写文档注释

8 通用程序设计

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

  1. 在第一次使用它的地方声明,并对其初始化
  2. 使方法小而集中

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

三种情况无法使用for-each循环

  1. 过滤
  2. 转换
  3. 平行迭代

item 47 了解和使用类库

不要重新发明轮子

item 48 如果需要精确的答案,请避免使用float和double

item 49 基本类型优先于装箱基本类型

基本类型和装箱基本类型的区别:

  1. 基本类型只有值,而装箱基本类型则具有与它们的值不同的同一性
  2. 装箱基本类型有null
  3. 基本类型通常比装箱基本类型更节省时间和空间

item 50 如果其他类型更适合,则尽量避免使用字符串

如果可以使用更加合适的数据类型,或者可以编写更加适当的数据类型,就应该避免用字符串来表示对象

item 51 当心字符串连接的性能

应该使用StringBuilder的append方法

item 52 通过接口引用对象

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

不存在适当接口类型的情形:

  1. 值类等(String、BigInteger、Random)
  2. 对象属于一个框架,而框架的基本类型是类,不是接口
  3. 类实现了接口,但是它提供了接口中不存在的额外方法

item 53 接口优先于反射机制

反射机制的缺点:

  1. 丧失了编译时类型检查的好处
  2. 执行反射访问所需要的代码非常笨拙和冗长
  3. 性能损失

item 54 谨慎地使用本地方法

在使用本地方法之前务必三思

item 55 谨慎地进行优化

item 56 遵守普遍接受的命名惯例

9 异常

item 57 只针对异常的情况才使用异常

item 58 对可恢复的情况使用受检异常,对编程错误使用运行时异常

item 59 避免不必要地使用受检的异常

item 60 优先使用标准的异常

item 61 抛出与抽象相对应的异常

item 62 每个方法抛出的异常都要有文档

item 63 在细节消息中包含能捕获失败的信息

item 64 努力使失败保持原子性

item 65 不要忽略异常

10 并发

item 66 同步访问共享的可变数据

同步不仅可以阻止一个线程看到对象处于不一致的状态之中,它还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前所有的修改效果。

如果读和写操作没有都被同步,同步就不会起作用。

避免本条目中所讨论的问题的最佳办法是不共享可变的数据。要么共享不可变的数据,要么压根不共享。

item 67 避免过度同步

为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法。

item 68 executor和task优先于线程

现在工作单元和执行机制是分开的,关键的抽象是工作单元,称作任务(task),任务有两种:Runnable及其近亲Callable。执行任务的通用机制是executor service。

item 69 并发工具优先于wait和notify

java.util.concurrent中更高级的并发工具分成3类:Executor Framework、并发集合以及同步器

如果你在维护使用wait和notify的代码,务必确保始终是利用标准的模式从while循环内部调用wait。一般情况下,你应该优先使用notifyAll,而不是使用notify。

item 70 线程安全性的文档化

item 71 慎用延迟初始化

大多数的域应该正常地进行初始化,而不是延迟初始化。如果为了达到性能目的,或者为了破坏有害的初始化循环,而必须延迟初始化一个域,就可以使用相应的延迟初始化方法。

  • 对于实例域,就使用双重检查模式
  • 对于静态域,则使用lazy initialization holder class idiom
  • 对于可以接受重复初始化的实例域,也可以考虑使用单重检查模式

item 72 不要依赖于线程调度器

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

如果线程没有在做有意义的工作,就不应该运行。

不要依赖Thread.yield或者线程优先级

item 73 避免使用线程组

如果你正在设计的一个类需要处理线程的逻辑组,或许就应该使用线程池executor

11 序列化

将一个对象编码成一个字节流,称作将该对象序列化,相反的处理过程称作反序列化。

item 74 谨慎地实现Serializable接口

实现Serializable的代价

  • 一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性
  • 它增加了出现Bug和安全漏洞的可能性
  • 随着类发行新的版本,相关的测试负担也增加了

为了继承而设计的类应该尽可能少地去实现Serializable接口,用户的接口也应该尽可能少地继承Serializable接口。

如果超类没有提供可供访问的无参构造器,子类也不可能做到可序列化。

item 75 考虑使用自定义的序列化形式

如果一个对象的物理表示法等同于它的逻辑表示法,可能就适合于使用默认的序列化形式。

如果你确定了默认的序列化形式是合适的,通常还必须提供一个readObject方法以保证约束关系和安全性。

当一个对象的物理表示法与它的逻辑数据内容有实质性的区别时,使用默认序列化形式会有以下4个缺点:

  1. 它使这个类的导出API永远地束缚在该类的内部表示法上
  2. 它会消耗过多的空间
  3. 它会消耗过多的时间
  4. 它会引起栈溢出

如果你正在使用默认的序列化形式,并且把一个或者多个域标记为transient,则要记住,当一个实例被反序列化的时候,这些域将被初始化为它们的默认值

不管你选择了哪种序列化形式,都要为自己编写的每个可序列化的类声明一个显式 的版本UID

item 76 保护性地编写readObject方法

  • 对于对象引用域必须保持为私有的类,要保护性地拷贝这些域中的每个对象。不可变类的可变组件就属于这一类别
  • 对于任何约束条件,如果检查失败,则抛出一个InvalidObjectException异常。这些检查动作应该跟在所有的保护性拷贝之后。
  • 如果整个对象图在被反序列化之后必须进行验证,就应该使用ObjectInputValidation接口
  • 无论是直接方式还是间接方式,都不要调用类中任何可被覆盖的方法。

item 77 对于实例控制枚举类型优先于readResolve

你应该尽可能地使用枚举类型来实施实例控制的约束条件。如果做不到,同时又需要一个既可序列化又是实例受控的类,就必须提供一个readResolve方法,并确保该类的所有实例域都为基本类型,或者是transizent的

item 78 考虑用序列化代理代替序列化实例

每当你发现自己必须在一个不能被客户端扩展的类上编写readObject或者writeObject方法的时候,就应该考虑使用序列化代理模式。要想稳健地将带有重要约束条件的对象序列化时,这种模式可能是最容易的方法。

相关文章

网友评论

      本文标题:《Effective Java》总结

      本文链接:https://www.haomeiwen.com/subject/rnrgfxtx.html