Java访问权限控制机制用于控制类、方法、域等Java元素的使用权限。Java语言通过多层次的访问权限控制,实现对访问权限的精确的控制。如果把方法和域看做是服务的话,那么不同的访问权限就像不同的服务级别一样,有普通服务、会员服务、钻石VIP服务等等,每种服务的级别是不一样的。那么,Java是如何实现访问权限控制的呢?
主要通过包机制、访问控制符(modifier)这两种机制来实现。包不仅是一种命名机制,也是一种权限控制机制,而访问控制符有多个层次,从来访问控制符到构造器访问控制符、方法控制符、域控制符等。通过多个层次,实现精准控制。由于域一般都设为private,因此本文在类成员的访问控制上,主要方法的调用为例子来说明。
接下来按照【包与类访问控制符-->构造器访问控制符-->方法访问控制符】这一层次顺序进行介绍。访问控制符,也叫修饰符(modifier)在下文简称控制符。
1. 包与类访问控制符:限制类型访问权限
包是对一种java类型(types,包括class、interface、enum、annotation)的组织方式,要使用包中的成员,首先你必须要有包的访问权限。因此,对于包中的types,Java通过是否使用访问控制符来决定外界(包以外)是否可以使用这些类型。顶级的types只有两种访问控制符:public和default(即没有控制符)。
如果类型声明为public,那么在包外和包内都可以使用这个类型。这里的使用,是指能否用import语句将其导入。至于导入之后如何使用,例如实例化、调用静态方法、访问静态域等。则要根据下一个层次(具体地说是构造方法和其他方法的控制符)的访问控制来决定。
如果不加访问控制符,即default。那么这个类型只能在本包中使用,具体能够进行哪个层次的使用,仍然需要有下一个层次的控制符来决定。
2. 构造器控制符:限制实例化
经过了包和类控制符这一层过滤之后,能够对类型进行什么程度的使用,取决于构造器控制符。构造器的控制符有4个层次:private、default、protected、public。
2.1 class控制符为public的情况下:
class控制符为public,意外着目前为止全世界都可以使用该类型。进入这个层次后,可以访问该类型的静态成员,通过类直接调用,无需通过对象。但是具体能够访问哪些静态成员,还必须由静态成员的控制符来决定。另外,还可以将类型用于instanceof操作,总之,除了不能实例化(以及以实例化为前提的其他操作),直接使用类型是类型是允许的,比如instanceof,比如调用ClassName.class等。但是能否对类型进行实例化,进而调用类型的实例方法、访问实例域,则需要由构造器进一步筛选。
2.1.1 private构造器:
如果将构造器声明为private,意味着对该类型的使用到此为止(只能访问静态成员)。不能使用该类型的这个构造器。也就是说,某个构造器private之后,就无法通过该构造器实例化对象。但仍然可以通过其他重载的,不是private的构造器进行实例化(下面讨论这种情况)。没有对象,也就没办法访问实例成员了。
2.1.2 default构造器:
class为public的前提下,如果构造器没有访问控制符,则只有本包中的类型能够实例化该类型。包以外的类型无法实例化。
2.1.3 protected构造器:
如果构造器声明为protected,则除了本包的类型可以对该类进行实例化以外,因此class的控制符为public,所以其他包中的类型也可以直接创建该类型的对象。
2.1.4 public构造器:
public class + public constructor,全世界都能实例化!!
2.2 class控制符为default的情况下:
这时只有本包中的类型可以使用该类型,包外的所有类型无法使用该类型。因此,根据构造器控制符不同,如下讨论:
2.2.1 private构造器:
一样的,即使是本包也不能实例化,谁都不行,除了本类!!
2.2.2 default构造器:
本来class为default就限制了只有本包的类能使用该类型,所以只要是本包的类型,都可以进行实例化。对于protected、public的情况,由于只有本包的类才能使用该类型,所有本包之内的都可以自由实例化该类型。
3. 方法(域)控制符:限制成员访问
3.1 静态方法控制符
前文提到,静态成员的访问不需要通过对象来进行,因此,对于能够使用该类型的所有类型来说,可以访问静态成员,但是受到静态成员的控制符的限制。在能够使用该类型的前提下,四种访问控制符按照常规方式决定能否访问静态成员。
3.2 实例方法控制符
对于实例方法或者实例域,必须通过对象来操作,因此能够使用实例方法的基本前提是能够实例化。(需要说明的是,如果能够导入该类型,那么也可以通过参数将类型的对象传入到本类中,进而调用实例方法。)能否实例化由第二部分中的构造函数控制符决定,能够实例化之后,再根据实例对象的控制符,决定是否可以调用实例方法。
4. 总结
通过上面介绍的三个层次,实现对访问权限的精确控制。总结一下:根据上面的介绍,总结一张表如下:
可以总结一下几条:
1)能否导入到类型中是能否访问静态成员和实例成员的基础条件。不能导入,就不可能访问类型的静态成员和实例成员。
2)能否导入某个类型由类型的class modifier决定。public class可以在任意地方导入,而default class只有在本包内才能导入。
3)能否访问静态成员由能否导入决定,同时受静态成员控制符影响,但与构造器控制符无关。
4)能否实例化对象由能否导入和构造器控制符决定。在此基础上,能否访问实例成员由实例成员控制符决定。
5)上表没有考虑的情况,只要能够导入类型,便可以声明该类型的变量,通过方法的参数传入该类型的对象,也可以访问实例成员(受成员控制符限制)。
6)能否访问实例成员除了受实例成员控制符限制外,其基本前提是能够持有对目标类型的对象的引用。而持有目标类型对象的引用有2中方式,
第一种是自己在类中创建,这一方法要求能够实例化对象,因此受到目标类型构造器控制符的影响。
第二种方法是传入目标类型对象的引用,这个方法首先要求能够导入目标类型,进而声明目标类型的变量,然后通过方法参数传入对象,实现对实例成员的访问。
7)Java通过包机制、访问控制符(包括类访问控制符,构造器访问控制符,成员访问控制符)实现对访问权限的精准控制。
8)设计类型或者类库的时候,如何科学地使用包和访问控制符,是一门大学问。例如,如果将类声明为default,那么将构造器声明为public和protected、default效果完全一样的,如何选择,还有待进一步实践。