类和接口-最小化类及其成员的可访问性

323 阅读3分钟

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

前言

设计良好的模块和设计不好的模块如何区分,最重要的因素只有一个,那就是这个模块对于外部其他模块而言,是否隐藏其内部数据和其他细节。设计良好的模块会隐藏所有的实现细节,API和它的实现清晰的隔离起来,模块之间只通过它们的API进行通信,也就是我们常说的封装。

封装的原因

最重要的是帮助系统中的模块互相隔离,你的结构对于我来说是一个黑盒,我看到的只有你的API,带来的好处就是可以由多个程序员同时来进行系统的开发而不会互相影响了。这些模块可以在其内部进行优化,只要对外的API保持不变,对于其他的模块就没有影响。

设计类和成员的基本原则

  1. 尽可能使每个类或者成员不被外界访问,尽可能使用最小的访问级别;对于最顶层的非嵌套类和接口,只有 package-private and public可选,能用package-private就用package-private。对于成员(域、方法、嵌套类或者嵌套接口)有四种访问级别:
  • private--在该类中私有访问;
  • 默认级别--包级访问;
  • protected访问级别--该类的子类或者包类所有类均可访问到;
  • public--在任何地方均可访问到;
  1. 如果类中覆盖了父类中的方法,那么子类中的访问级别不得低于父类中的访问级别,这样就可以保证在任何使用到父类实例的地方可以继续使用子类。特别之处是如果一个类实现了某接口,那么在类中所有的接口的方法都必须是public的;
  2. 实例域(Instance fields)决不能是public的,如果是public,那么你就相当于再也无法控制这个实例域的数据变动,即使被赋进来一个错误的数据你也只能忍受。如果非final实例域指向了可变对象,并且该实例域为public的话,那么包含该实例域的类就是线程不安全的;
  3. 静态字段(static fields)也不能是public的,只有一个例外,public static final fields 可以使用,用来表示类的不可变常量,需要注意的是,一个非零长度的数组永远可以编辑,用户可以编辑其中的数据,所以永远不要提供这种变量出去。
public static final Thing[] VALUES = { ... };

应该使用

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

Collections.unmodifiableList可以保证类不会被修改。

或者

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

总结

总之,在设计类和成员时,应该尽可能的降低可访问性,除了 public static final fields的做常量这种特殊情形之外,公有类都不应该包含公有域,确保公有的静态final域所引用的对象是不可变的。