一、信息隐藏(封装)
软件设计基本原则之一:信息隐藏(封装)是说良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰地隔离开,模块之间只通过它们的API进行通信。
信息隐藏的好处:
1、有效解除组成系统的各模块之间的耦合关系
2、有效地调节性能
3、提高了软件的可重用性
4、降低了构建大型系统的风险
二、访问控制
访问控制机制决定了类、接口和成员的可访问性。实体的可访问性是由该实体声明所在的位置,以及该实体声明中所出现的访问修饰符共同决定的。正确地使用这些修饰符对于信息隐藏至关重要。
尽可能地使每个类或者成员不被外界访问。 换句话说,应该使用与你正在编写的软件的对应功能相一致的、尽可能最小的访问级别。
顶层(非嵌套的)的类和接口:
对于顶层(非嵌套的)的类和接口,只有两种可能的访问级别:包私有和公有的。如果你用public修饰符声明了顶层类或者接口,那它就是公有的;否则,它将是包私有的。
如果一个顶级类或者接口能够设置成包私有,它的访问级别就应该是包私有的。通过把类或者接口设置成包私有,它做成了实现的一部分而不是导出的API,在之后的发行版本中,可以对它进行修改、替换,或者删除,而无需担心会影响到现有的客户端。如果你把它做成公有的,你就有义务永远支持它,以保持它们的兼容性。
如果一个包级私有的顶层类或者接口只被一个类使用,请考虑将它成为唯一使用它的那个类的私有嵌套类。这样就可以将它的可访问范围从包中的所有类缩小到了使用它的那个类。但是,降低不必要共有类的可访问性,比降低包级私有的顶层的更重要得多:因为共有类是包的API的一部分,而包级私有的顶层类则已经是这个包的实现的一部分。
/**
* public修饰符声明了顶层类或者接口,那它就是公有的,
* 共有类是包的API的一部分,
* 你就有义务永远支持它,以保持它们的兼容性
*/
public class Test {
}
/**
* 沒有修饰符是包级私有的
* 不是是包的API的一部分,
* 在之后的发行版本中,可以对它进行修改、替换,或者删除,而无需担心会影响到现有的客户端。
*/
class Test {
}
对于成员(域(字段)、方法、前套类和嵌套接口)有四种可能的访问级别(按照可访问性的递增顺序):
1、私有的(private) —— 只有在声明该成员的顶层类内部才可以访问这个成员。
2、包级私有的(package-private) —— 声明该成员的包内部的任何类都可以访问这个成员。从技术上讲,它被称为“缺省(default)访问级别”,如果没有为成员指定访问修饰符,就采用这个访问级别(除了接口的成员,接口成员默认是公有的)。
3、受保护的(protedted) —— 声明该成员的类的子类可以访问这个成员,并且,声明该成员的包内部的任何类也可以访问这个成员。
4、公有的(public) —— 在任何地方都可以访问该成员。
一般来说,私有成员以及包级私有成员都是一个类的实现中的一部分,不会影响它的导出的API。
子类覆盖超类方法时,访问级别不允许低于超类的访问级别。实现接口时,那么所有的类方法在这个类必须全部声明为公有的(接口中所有方法都隐含公有访问级别)。
实例域决不能是公有的。静态域也是如此但是静态域有一个例外,假设常量构成了类提供的整个抽象中的一部分,可以通过公有的静态final域来暴露这些常量。按惯例,这些域的名称由大写字母组成,单词之间用下划线隔开,这些域要么包含基本类型,要么包含指向不可变对象的引用。
工作中:经常会有一些常量是固定值的,我们一般会建一个常量类来定义这些常量。
public class MessageTypeConstant {
public static final String TEMPLATE_MSG = "msg";
}
如果静态final域包含对可变对象的引用,它便具有非final属性的所有缺点。 虽然引用本身不能被修改,但引用的对象可以被修改 (这会带来灾难性的结果)。
注意,长度非零的数组总是可变的,所以类具有公共的静态final数组域,或者返回这种域的访问方法这几乎错误的。 如果类具有这样的域或访问方法,客户端将能够修改数组的内容。 这是安全漏洞的常见来源。
三、总结:
总而言之,你应该始终尽可能地降低可访问性。 你在仔细设计了一个最小的公有API之后,应该防止任何散乱的类,接口或成员变成API的一部分。 除了公共静态final域之外,公共类不应该有公共属性。并且 确保公有静态final域(public static final)所引用的对象都是不可变的。