EffectiveJava第4章-类和接口

第13条:使类和成员的可访问性最小化

这个被称为信息隐藏或封装。它可以解耦,使模块可以独立开发、测试、优化、使用、理解和修改。可以让开发人员单独调试自己的程序,提高开发系统的速度,程序有问题的时候,可以准确的定位在哪个模块出现问题,降低了构建大型系统的风险。

访问控制机制决定了类、接口和成员的可访问性。
第一个规则:尽可能地使每个类或者成员不被外界访问。类和接口只有两种访问级别:包级私有(private)和公有的(public)。

对于成员(域、方法、嵌套类和嵌套接口)有四种可能的访问级别,按访问性递增顺序罗列:

  1. 私有的(private),只有在声明该成员的顶层类内部才可以访问这个成员。
  2. 包级私有的(package-private),声明改成员的包内部的任何类都可以访问这个成员。从技术上讲,它被称为“缺省(default)访问级别”,如果没有为成员指定访问修饰符,就采用这个访问级别。
  3. 受保护的(protected),声明该成员的类的子类可以访问这个成员,并且,声明该成员的包内部的任何类也可以访问这个成员。
  4. 公有的(public),在任何地方都可以访问该成员。

如果方法覆盖了超类的一个方法,子类中的访问级别就不允许低于超类中的访问级别。这样可以确保任何使用超类的实例的地方都可以使用子类的实例。
接口中的方法默认就是共有的,所以一个类实现了一个接口,那么接口中所有的类方法在这个类中也都必须声明为公有的。

实例域绝对不能是公有的(第14条:在公有类中使用访问方法而非公有域)。

长度非零的数组总是可变的,

public static final String[] VALUES = {};  
    //公有数组变成私有的,并增加一个公有的不可变列表
    private static final String[] VALUES = {...};
    public static final List VALUES =               Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
    //或者返回私有数组的克隆
    private static final String[] VALUES = {...};
    public static final Thing[] values(){
        return PRIVATE_VALUES.clone();
    }
}

第14条:在公有类中使用访问方法而非公有域

如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改变该类的内部表示法的灵活性。

如果类是包级私有的,或者私有的嵌套类,直接暴露它的数据域并没有本质的错误。

第15条:使可变性最小化

不可变类只是其实例不能被修改的类,例如String,基本类型的包装类、BigInteger和BigDecimal。不可变的类比可变的类更加易于设计、实现和使用。它们不容易出错,且更加安全。

要使类成为不可变,要遵守下面五条规则:

 1. 不要提供任何会修改对象状态的方法。

 2. 保证类不会被扩展。一般是把类定义为final的,或者将类的所有构造器都变成私有的或者是包级私有的,并添加静态工厂来代替共有的构造器。

 3. 使所有的域都是final的。

 4. 使所有的域都成为私有的。

 5. 确保对于任何可变组件的互斥访问。确保客户端无法获得对象的引用。

不可变对象本质上是线程安全的,它们不要求同步。

不可变对象可以被自由地共享,不仅可以共享不可变对象,甚至也可以共享它们的内部信息。

不可变对象为其他对象提供了大量的构件

不可变对象真正唯一的缺点是,对于每个不同的值都需要一个单独的对象。

不可变的对象内部可以拥有一个或者多个非final得域,来缓存一些值(hashcode,String中就有),不变形保证了这个值永远都不会变。
基本类型的域不能用final,因为在初始化的时候,会被赋初始值。

第16条:复合优先于继承

本书使用的继承指的是实现继承,即一个类扩展另一个类的时候。

继承打破了封装性。子类依赖超类的特定功能的实现,如果超类发生变化,子类必须跟着超类一起改变。这样的子类是非常脆弱的。

只有当子类和超类之间确实存在子类型关系时(is-a),使用继承才是恰当的。

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

文档必须精确地描述覆盖每个方法所带来的影响。

对于每个公有的或者受保护的方法或者构造器,它的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续的处理过程的。

好的API文档应该描述一个给定的方法做了什么工作,而不是描述它是如何做到的。

为了继承而设计的类,唯一的测试方法就是编写子类。

为了允许继承,类还必须遵守其他一些约束:
1.构造器不能调用可被覆盖的方法
2.如果为了继承而设计的类实现了Cloneable或者Serializable接口,clone和readObject都不可以调用可覆盖的方法,不管是以直接还是间接的方式。
3.如果为了继承而设计的类实现了Serializable接口,并且该类有一个readResolve或者writeReplace方法,就必须使readResolve或者writeReplace成为受保护的方法,而不是私有的方法。

消除类中可覆盖方法的自用特性。就可以安全的子类化。

第18条:接口优于抽象类

1.现有的类可以很容易被更新,以实现新的接口。比如Comparable
2.接口是定义混合类型的理想选择。混合类型表明具有某个可供选择的行为。
3.接口允许我们构造非层次结构的类型框架。

骨架实现类就是一个抽象类继承了某一个接口

第19条:接口只用于定义类型

常量接口:没有包含任何方法,只包含了一些静态的final域,每个域都导出一个常量。常量模式是对接口的不良使用

如果要导出常量,就应该把这些常量添加到这个类。
使用枚举类型或者不可实例化的工具类。

第20条:类层次优于标签类

类层次可以反映类型之间本质上的层次关系。

第21条:用函数对象表示策略

函数指针的只要用途就是实现策略模式。
在java中实现这种模式,首先要声明一个接口来表示该策略,并且为每个具体策略声明一个实现了该接口的类。当一个具体策略只被调用一次的时候,通常使用匿名类来声明和实例化这个具体的实现类。当一个具体策略是设计用来重复使用的时候,它的类通常就要被实现为私有的静态成员类,并通过公有的静态final域被导出,其类型为该策略接口。

第22条:优先考虑静态成员类

见另一篇文章《Java嵌套类》。

你可能感兴趣的:(EffectiveJava第4章-类和接口)