[读书笔记]《Effective Java》第二章

2017年02月05日 9571点热度 2人点赞 1条评论

Item 1: 使用静态工厂方法而不是构造函数

优势:

  1. 有名字
    比如BigInteger.probablePrime() 一看就知道是返回一个BigInteger素数.
    比较不好的做法是构造函数的重载, 通过修改参数的顺序达到不同的功能
  2. 不需要每次都创建新的对象. 比如如下代码:

 

如果创建类的开销比较大, 那么对性能的提升就更明显了

  1. 能返回的类型选择更多, 不一定是当前类的类型 这一点非常好理解~
  2. 在创建参数化类型实例的时候, 代码会更简洁 主要原理是类型推导(type inference), 例如 :

 

如果需要支持各种类型, 那么代码量级就海了去了, 同时也是很枯燥无聊的事情. 利用类型推导, 可以使用下面的方式:

 

这种方式在Hadoop与Spark之中的应用非常非常广泛.

注意: 这种方式在当时的JDK1.6 可能会比较好用, 但是现在(JDK 1.8)可以更加方便的进行创建了, 有下面两种方式:

 

劣势

  1. 除非类含有public或者protected构造函数, 否则这些静态方法不能被子类化
    好像这也没什么
  2. JavaDoc 上面没有特殊显示, 不能直接区分出跟其他普通方法的区别
    因为现在的IDE已经可以很完美的显示出不同了

本文原创,原文链接:http://www.flyml.net/2017/02/05/effective-java-chapter-1/

 

Item 2: 使用builder替代多个参数的constructor

有时候,我们不得不面对一个对象需要很多参数.
作者不太推荐的做法是构造函数重载的方式, 如下所示:

 

如果我们需要新建一个对象, 可能需要如下代码:

 

如果我们想知道每个参数的意义, 只能去构造函数的定义之中一个个的看.
如果参数列表更长, 这个过程可能会非常痛苦而且容易出错

可以使用JavaBean 的getters / setters 方法:

 

但是这种方法的不足在于: JavaBean 不是不可变的(immutable Item 15), 可能在创建过程之中状态不一致【这一点笔者还不够理解, 是因为线程安全?】.

这里应该使用Builder模式(Gamma95, p. 97)

先来看看结果:

 

这种方法, 首先需要创建一个builder 对象(上面第二行), 有非常轻微的性能损失.

可能要到Item 39/63 才能知道Builder模式相对setter 模式的优势, 相比构造函数, 优势就是非常灵活了.

比如在构造函数进行参数调整的时候, 不会牵一发而动全身.

只不过, 跟多setter方法类似, 个人认为这种方式不知道有多少个参数要全部实现才行. 如果是构造函数, 看一眼就知道了.

本文原创,原文链接:http://www.flyml.net/2017/02/05/effective-java-chapter-1/

Item 3: 使用私有构造函数/枚举类型强化单例属性(Singleton Property)

关于单例, 这里可能多说一些.
单例就是为了能够在整个环境之中,只有一份实例存在. 常见的应用场景, 比如FileSystem, WindowManager 等等.

在Java之中, 可以有至少7种单例的实现方法, 可以参考《Java:单例模式的七种写法》

在看的时候, 可以多看看下面各种大神的评论, 感觉并没有完全统一的定论, 不过此书作者的推荐跟StackOverflow上面推荐的做法都是一致的: 枚举类型

 

在使用的时候, 如下所示:

 

这种做法的好处:

  1. 有效避免多线程同步问题
  2. 防止反序列化重新创建新的对象

这里有一些更详细的说明: http://www.importnew.com/6461.html

PS: 只不过这种方式, 在实际工作之中确实太少见了. 可能是我孤陋寡闻~

PSS : 这里面好像跟私有构造函数没有什么关系? 但是标题里面确实提到了私有构造函数. 不明所以~

本文原创,原文链接:http://www.flyml.net/2017/02/05/effective-java-chapter-1/

Item 4: 通过私有构造函数使工具类无法被实例化

原始英文: Enforce noninstantiability with a private constructor
直译感觉比较绕: 使用私有构造函数强化不可实例化的能力
因此, 自行对全文内容作了一个片面的小结, 使得标题更加容易理解一些

我们在日常工作之中, 一般会生成各种各样的工具类, 在JDK之中, 比如java.util.Math.
还有一些并不是那么纯的工具类, 比如 java.util.Array.

对于这些工具类, 如果不显示的声明一个使用构造函数, 使用者可能就会无意识的将这个类new一下, 即实例化。 这种做法不是正确的使用方法, 而不是会造成什么性能问题吧。

注意: 在class之前使用final ,只是说不能继承, 而不是不能被实例化!

可以参考下面的做法:

本文原创,原文链接:http://www.flyml.net/2017/02/05/effective-java-chapter-1/

 

Item 5: 避免创建不必要的对象

文中列举了下面的对比例子:

 

文中认为, 在实际执行的时候, 每次都要new String() 会造成不必要的性能开销.
在现代编译器之中, 这两种方式是一样的, 因为在编译的时候会自动识别.

不过如果还有类似的其他情况, 如果有不需要变化的对象, 就省着点用. 可以对比下面两段代码:

不推荐的做法

 

推荐做法

 

【注意】文中也强调了几点:

  1. 不应该维护自己的对象池, 除了数据库连接(这个用开源的库好了, 除非您也是大神)
  2. 普通对象的创建与销毁是非常轻量级的, 鼓励多创建对象

本文原创,原文链接:http://www.flyml.net/2017/02/05/effective-java-chapter-1/

 

Item 6: 消除过期的引用

这一章主要谈了内存泄露
首先文章里面用了一个自己写的Stack类做例子来说明内存泄露的情况。 核心在于: Stack类自己维护了一个数组来存储对象(对象存储池), 当pop之后, 不再需要的那个对象, 并没有手动置空。 从GC/JVM 的角度来说, 这个对象依然是被需要的(因为还被引用的状态)

置空很简单, 如下面第5行

 

Item 7: 避免使用finalizer

具体不是非常理解, 但是结论还是比较简单的:
不要自行使用System.gc() or super.finalize() 或者类似的方法 不要去覆盖finalize()方法

本文为原创文章,转载请注明出处
原文链接:http://www.flyml.net/2017/02/05/effective-java-chapter-1/

 

 

RangerWolf

保持饥渴的专注,追求最佳的品质

文章评论