区别设计良好的模块和设计很差的模块,最重要的因素在于,这个模块对于外部的其余模块而言,是否隐藏其内部数据和其余实现细节。设计良好的模块会隐藏全部的实现细节,把它的API于它的实现清晰地隔离开来。而后,模块之间经过它们的API进行通讯,一个模块不须要知道其余模块的内部工做状况,这个概念称为信息隐藏或封装。
使类和成员的可访问性最小化能够有效的解除系统中各个模块的耦合度、实现每一个模块的独立开发、使得系统更加的可维护,更加的健壮。
对于顶层的(非嵌套的)类和接口,只有两种可能的访问级别,包级私有的和公有的。若是类或者接口能够被作成包级私有,就应该被作成包级私有,这样它实际上成了包的实现的一部分,而不是该包导出的API的一部分,在之后的发行版本中,能够对它进行修改、替换、删除,而无需担忧影响现有的客户端程序。若是被作成公有的,就有责任永远支持它,以保持兼容性。
若是一个包级私有的顶层的类(或者接口)只是在某一个类的内部能够被用到,就应该考虑使它成为惟一使用它的那个类的私有嵌套类。这样能够进一步缩小可访问范围。api
对于成员(域、方法、嵌套类和嵌套接口)有四种可能的访问级别:
私有的(private)——只有在声明该成员的顶层类内部才能够访问这个成员。
包级私有的(package-private)——声明该成员的包内部的任何类均可以访问这个成员。若是没有为成员指定访问修饰符,就采用这个访问级别,“缺省”
受保护的(protected)——声明该成员的子类能够访问这个成员(即便不在同一包),而且声明该成员的包内部任何类也能够访问这个成员
公有的(public)——在任何地方均可以访问该成员数组
私有成员和包级成员都是一个类的实现中的一部分,通常不会影响它导出的API,然而,若是类实现了Serializable接口,这些域就有可能被“泄漏”到导出的API中。
有一条规则限制了下降方法的可访问性的能力,若是方法覆盖了超类的一个方法,子类的访问级别不容许低于超类中的访问级别。这样能够确保任何可以使用超类的实例的地方均可以使用子类的实例。好比,若是一个类实现了一个接口,那么接口中全部的类方法在这个类中也都必须被声明为公有的(接口的全部方法隐含公有访问级别)。
实例域决不能是公有的。若是域是非final,或者是一个指向可变对象的final引用,那么一旦使这个域成为公有的,就放弃了对存储在这个域中的值进行限制的能力。所以,包含公有可变域的类不是线程安全的。即便域是final的,而且引用不可变的对象,当把这个域变成公有的时候,也就放弃了“切换到一种新的内部数据表示法”的灵活性(由于公有的是导出API,必须始终坚持这种数据表示,保证兼容)。
安全
虽然实例域决不能是公有的,但有一种状况是例外的,假设常量构成了类提供的整个抽象中的一部分,咱们是能够经过公有的静态final域来暴露这些常量,可是,若是final域包含可变对象的引用,它便具备非final域全部的缺点,虽然引用的自己是不能修改的,可是它所引用的对象却能够在客户端被修改,这是很严重的安全后果,例以下面性能
public static final Person[] VALUES = new Person[]{ new Person("aa", 21, 'M'),new Person("c", 11, 'M'),new Person("d", 31, 'M') };
虽然咱们不能对VALUES中Person的引用进行修改,可是咱们却能够修改引用指向Person对象中的属性值,如VALUES[0].setName("修改了名字");那么该如何作才会避免这种安全漏洞呢,修正方法有两种,下面咱们来看下。测试
1.使公有数组变成私有的,并增长一个公有的不可变列表:spa
private static final Person[] PRIVATE_VALUES = new Person[]{ new Person("aa", 21, 'M'),new Person("c", 11, 'M'),new Person("d", 31, 'M') }; //unmodifiableList,生成一个不可修改的List public static final List<Person> VALUES= Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
2.使公有数组变成私有的,并添加一个公有方法,返回私有数组的一个备份:线程
private static final Person[] PRIVATE_VALUES = new Person[]{ new Person("aa", 21, 'M'),new Person("c", 11, 'M'),new Person("d", 31, 'M') }; public static final Person[] values() { return PRIVATE_VALUES.clone(); }
在这两种方法选择,得考虑客户端可能怎么处理这个结果,哪一种返回类型更加方便,哪一种会获得更好的性能。设计
最后总结下如何最小化类和接口的可访问性:
(1)首先设计出该类须要暴露出来的api,而后将剩下的成员的设计成private类型。而后再其余类须要访问某些private类型的成员时,在删掉private,使其变成包级私有。若是你发现你须要常常这样作,那么就请你从新设计一下这个类的api。
(2)对于protected类型的成员,做用域是整个系统,因此,能用包访问类型的成员的话就尽可能不要使用保护行的成员。
(3)不能为了测试而将包中的类或者成员变为public类型的,最多只能设置成包级私有类型。
(4)实例域绝对不能是public类型的。code