`
gdwrx_winson
  • 浏览: 131492 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Effictive Java阅读笔记

    博客分类:
  • Java
阅读更多

避免创建重复的对象

面向范围:非可变的对象、已知不会被修改的可变对象

用法:如果有静态工厂方法----静态工厂方法。

      在静态区域初始化---将一些对象改成static

      适配器(adapter)/视图(view):把功能委托给后面的一个对象,从而为后面的对象提供一个可选的接口的对象。针对某个给定对象的特定适配器而言,不需要创建多个适配器实例。Map接口的keySet方法不是每次返回都创建一个新的Set实例。

      数据库连接池的使用

 

注意:小对象一般采取创建新的对象,大的对象才采取重用。

 

 

避免使用终结函数

原因:终结函数(finalizer)是不可预测,危险的,一般情况下不必要。会导致不稳定行为、更差的性能,以及移植性问题。

      终结函数不能保证会被及时地执行,执行时间点依赖与JVM的实现。

      System.gcSystem.runFinalization只是增加终结函数被执行的机会。

      如果一种未被捕获的异常会在终结过程中被抛出来,那么这种异常就被忽略了,同时这个对象已经被破坏却不通知其他线程(对象)

      提供一个显式的终止方法。如closecancel,结合try-finally结构使用。

 

好处:当一个对象的所有者忘记了调用显式终止方法的情况下,终结函数可以充当“安全网”。

      在本地对等体不拥有关键资源的前提下,终结函数是最合适的。----本地对象,普通对象通过本地方法委托给一个本地对象,在普通对象回收的时候,垃圾回收器不知道普通对等体。

 

注意:终结函数链不会被自动执行---需要手工调用超类的终结函数(结合try-finally)

      终结函数守卫者----把终结函数不是放在要求处理的类中,而是放在匿名类中,该匿名类的唯一用途就是终结其外围实例。当守卫者被终结的时候,它执行外围实例所期望的终结行为。

 

改写equals时总要改写hashCode

原因:导致该类无法与所有基于散列值的集合类结合在一起正常运作。(HashMapHashSetHashtable)

 

规范:在一个应用程序执行期间,对象调用hashCode方法多次,必须始终返回同一个整数。

      如果两个对象根据equals(Object)是相等的,那么这两个对象调用hashCode都返回同一个整数。

      如果两个对象根据equals(Object)是不相等的,那么这两个对象调用hashCode不要求返回不同整数。但返回不同整数可以提高散列表性能。

 

散列函数的建议:将各个域组织起来形成一个表达式

                如果类是非可变的,且计算散列码代价比较大,可以考虑把散列码缓存在对象内部。

                    private volatile int hashCode = 0;

                    public int hashCode() {

                        if(hashCode == 0) {//生成新的散列码}

                    }

 

谨慎地改写clone

Cloneable做了什么?---如果一个类实现了Cloneable,则Objectclone方法返回该对象的逐域拷贝,否则的话抛出CloneNotSupportedException异常。

 

规范:对任何对象x

        x.clone() != x

        x.clone().getClass() == x.getClass()

        x.clone().equals(x)

      都为true

 

注意:clone是浅复制的,可以通过建立类似于自动的构造函数链的方式最终递归到Objectclone---要求一系列的父类都要实现clone

      可以在clone里面自己创建新的对象赋值---final域产生冲突。

      所有实现Cloneable接口的类都应该用一个公有的方法改写clone。此公有方法首先调用super.clone,然后修正任何需要修改的域。

 

替代方案:拷贝构造函数

             public A(A a);

          静态工厂代替构造函数

             public static A newInstance(A a);

 

考虑实现Comparable接口

java.lang.Comparable接口唯一方法:compareTo()---允许进行简单的相等比较和执行顺序比较。

实现Comparable接口,表明它的实例具有内在的排序关系。

 

好处:可以跟许多泛型算法以及依赖于该接口的集合实现进行协作。

 

规范:当该对象小于、等于或者大于指定对象的时候,分别返回一个负整数、零或者正整数。

      对称性、传递性

 

建议:(x.compareTo(y)==0)  ==  (x.equals(y))---BigDecimal类就不符合该建议。

      如果想为一个实现了Comparable接口的类增加一个关键特征,不要去扩展这个类,而是编写一个不相关的类,其中包含一个域,其类型为第一个类,然后提供一个视图方法返回这个域。----可以自由地在第二个类上实现compareTo,可以将第二个类的实例当做第一个类的实例看待。

      如果一个域没有实现Comparable接口,或者需要使用一个非标准的排序关系---使用显式的Comparator

 

依赖于CompareTo比较的类:TreeSetTreeMapCollectionsArrays

 

清除过期的对象引用

过期引用:指永远不会再被解除的引用。

用法:将对象引用赋值null

      重用一个本来已经包含对象引用的变量,后者让这个变量结束其生命周期。---要想引用消失,必须退出包含该变量的方法。

例子:在数组非活动区域存在对对象的引用---垃圾回收器不会自动处理,需要手工将对象引用清空。

      将对象遗忘在缓存中---只要在缓存之外存在对某个条目的键的引用,该条目就有意义----使用WeakHashMap代表缓存。

                        ---缓存中条目过期之后自动被删除

                        ---缓存中的条目在运行的过程中变得越来越没有价值----使用LinkedHashMap类的removeEldestEntry方法实现。

 

注意:清空对象引用是一种例外行为而非一种规范行为。

 

使用私有构造函数强化singleton属性

定义:指只能实例化一次的类。通常用于代表那些本质上具有唯一性的系统组件。

用法:公有静态成员:

      public class A {

         public static final A INSTANCE = new A();

         private A() {...}

      }

      公有静态工厂方法:

      public class A {

         private static final A INSTANCE = new A();

         private A() {...}

         public static A getInstance() {

             return INSTANCE;

         }

      }

如果确信该类永远是一个singleton,那么使用第一种方法有意义。

如果希望保留一点修改余地,那么使用第二种方法。

 

注意:为使一个singleton类变成可序列化,仅仅在声明中加上implements Serializable是不够的。为维护singleton性,必须也要提供一个readResolve方法。否则的话,一个序列化的实例在每次反序列话的时候,都会导致创建一个新的实例。

        private Object readResolve() throws ObjectStreamException {

                   return INSTANCE;

         }

 

通过私有构造函数强化不可实例化的能力

定义:提供一个显式的私有构造函数,同时不提供方法生成对象。

用法:public class A {

          private A() {}

      }

好处:只包含静态方法和静态域的类(工具类)---java.lang.Mathjava.util.Arraysjava.util.Collections

缺点:类不能被子类化---不存在可访问的超类构造函数。

 

用静态工厂方法代替构造函数

定义:一个静态方法,返回类的一个实例。

用法:public static Boolean valueOf(boolean b) {

         return (b ? Boolean.TRUE : Boolean.FALSE);

      }

好处:静态工厂方法有名字,便于阅读。绕开一个类只有一个原型相同的构造函数----不同的工厂方法名(如不用静态工厂方法,可以通过参数类型顺序不同来实现多个构造函数)

      每次被调用的时候,不要求非得创建新的对象,故对于一些非可变类来说,可以使用一个预先构造好的实例,避免频繁创建相同对象。(好处:singleton、非可变类的equals比较简化成==)

      返回类型比较灵活,可以返回原类型的子类型对象。

缺点:类如果不含公有的或者受保护的构造函数,就不能被子类化(被继承)---使用复合结构而非继承

      不是规范----标准命名习惯----valueOfgetInstance

 

在改写equals的时候请遵守通用约定

原因:Object类的equals只是简单地比较两个对象的引用是否相同。

      类需要实现“逻辑相等”概念---超类并没有实现或不适合

 

一般不用改写的情况:类型安全枚举类型----保证了每一个值至多只存在一个对象。

                    String

                    包装类---IntegerBoolean

 

改写规范:equals方法实现了等价关系

             自反性:x.equals(x)一定为true

             对称性:y.equals(x)true时,x.equals(y)一定为true

             传递性:x.equals(y)truey.equals(z)true,则x.equals(z)true

             一致性:多次调用z.equals(y)要么一致地返回true,要么一致返回false

             对任意非空引用值xx.equals(null)一定返回false

 

建议:使用==检查“实参是否为指向对象的同一个引用”---if (this==o) return true;

      使用instanceof检查“实参是否为正确的类型”

      把实参转换到正确的类型

      对类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。

      检验是否对称、传递、一致的。

 

注意:改写equals的时候,总要改写hashCode

      不要将equals追求过度的完美等价关系。

      不要使equals依赖于不可靠的资源。

      不要将equals声明中的Object对象替换为其他类型---那变成了重载而非重写。

 

总是要改写toString

原因:Object类返回的格式固定为---类名字+@+散列码的无符号十六进制表示。

      追求“简洁的,但信息丰富,并且易于阅读的表达形式”。

 

缺点:如果这个类已经被广泛使用,则一旦指定了格式,必须始终坚持这种格式。不指定格式可以保留灵活性。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics