EffectiveJava学习笔记(四)

147 阅读3分钟

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

软件设计的基本原则之一:信息隐藏--一个模块不需要知道其他模块的内部工作情况。 实现这个原则很简单,就是尽可能的使每个类或者成员不被外部访问。

对于顶层的类,只有两种访问级别:包级私有(package-private)、公有(public)。 一个类声明为包级私有时,它实际上是这个包的实现的一部分,而非外部接口,在以后的版本中可以自由的修改或删除。如果声明为公有的,则需要永远的支持它,维护它的兼容性。

  • 公有类的实例域绝不能是公有的。如果实例域是非final的或者指向一个可变对象的final引用,如果设置为公有的,就等于放弃了对存储在这个域中对值进行限制对能力。
  • 让类具有公有对final数组域或者返回数组域的返回方法,也是错误的,客户端将可以修改数组的内容。修正这个问题有两种方式:

使公有数组变为私有,增加公有的不可变列表

private static final String[] PRIVATE_VALUES = {...}
public static final List<String> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES);

数组变为私有,添加公有方法返回数组的拷贝

private static final String[] PRIVATE_VALUES = {...}
public static final String[] values(){
    return PRIVATE_VALUES.clone();
}

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

公有类永远不应该暴露可变的域。虽然还是有问题,但是让公有类暴露不可变的域,可通过添加约束条件降低危害性。有时会需要包级私有或私有嵌套类暴露域,无论这个类是可变的还是不可变的。

第17条:使可变性最小化

不可变类遵循以下五条原则:

  • 不要提供任何会修改对象状态的方法(也称设值方法)
  • 保证类不会被扩展
  • 声明所有域都是final的
  • 声明所有的域都为私有的
  • 确保对于任何可变组件的互斥访问。 如果类具有指向可变对象的域,必须确保使用该类的客户端无法获得指向这些对象的引用,永远不要用客户端提供的对象来初始化这样的域。

不可变对象特点:

  • 不可变对象比较简单,只有一种状态即被创造时的状态
  • 不可变对象本质上是线程安全的,不要求同步,可以自由的共享。
  • 不可变对象为其他对象提供了大量的构件
  • 缺点:对于每个不同的值都需要一个单独的对象,如果有一个上百万位的BigInteger,想要改变它的低位,就要创建一个新的BigInteger实例,也有上百万位,但是与原来的对象只有一位不同。(解决这个问题可以使用BigSet,可以在固定时间内仅改变单个位的状态) 如果无法预测客户端要在不可变类上进行哪些复杂的操作,可提供一个公有的可变配套类,如String-->StringBuilder

构造不可变类有两种方式,一种方法是使类成为final的,另一种方法是是让类的构造器变为私有的,并提供公有的静态方法来代替公有的构造器。

public class Point{
    private final int x;
    private final int y;
    private Point(int x,int y){
        this.x = x;
        this.y = y;
    }
    public static Point valueOf(int x,int y){
        return new Point(x,y);
    }
}

总结:

  1. 除非有很好的理由要让类成为可变的类,否则它就应该是不可变的
  2. 如果类不能做成不可变的,要尽可能的限制它的可变性
  3. 构造器应该创建完全初始化的对象,并建立起所有的约束关系