第六章 访问权限控制
简介
Java提供了访问权限修饰词,供类库开发人员向客户端程序员指明哪些是可用的,哪些是不可用的。访问权限控制的等级,从最大权限到最小权限一次为:public、protected、包(library)访问权限(没有关键词)以及private。
6.1 包(library):库单元
包内含有一组类,它们在单一的名字控件之下被组织在一起。例如,在Java的标准发布中有一个工具库,它被组织在java.util名字空间之下。java。util中有一个叫ArrayList的类,使用ArrayList的一种方式是用其全名java.util.ArrayList来指定。例如:
public class Test {
public static void main(String[] args) {
java.util.ArrayList list = new java.util.ArrayList();
}
}
有没有觉得很长很麻烦,可以用import关键字来导入该类,例如:
import java.util.ArrayList
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
}
}
我们需要通过导入来给类一个名字空间。如果同时有两个名字为Stack类并且在同一机器上面了,这时候需要对名称空间进行完全控制,给每个类贴上唯一的标签。
当编写一个Java源代码文件的时候,此文件通常被成为编译单元,每个编译单元都必须由一个后缀名.java,在编译单元的内部可以有一个由关键词public修饰的类,该类的名称必须和文件名相同,否则编译器报错。如果在编译单元里面有额外的类,这些类不是public的,在包之外无法被看见。
6.1.1 代码组织
当编译一个.java文件时,在文件中的每个类都会有一个输出文件,输出的文件名和类名称相同,后缀名为.class。因此在编译少量.java文件之后,可能会的到很多.class文件,如果用编译性语言(例如C),编译之后产生的中间文件(通常是一个obj文件),然后再通过链接器(用来创建一个可执行文件)或类库产生器(librarian,用来创建一个类库)产生的其他同类文件绑定一起。
但这不是Java的工作方式,Java可运行程序是一组可以打包并且压缩为一个Java文档文件(JAR,使用Java的jar文档生成器)的.class文件。Java解释器负责这些文件的查找、装载和解释。
类库实际上是一组类文件。其中每个文件都有一个public类,以及人以数量的非public类。如果希望.java文件从属于同一个群组,就可以用到package关键字。
package语句必须是一个编译单元中(.java文件)除了注释之外的第一行程序代码。例如package access;
。就表明该类是名为access的类库的一部分。例如
package access.mypackage;
public class MyClass {
.....
}
定义好了类MyClass并且为其指定好了包名之后,如果需要用到该类,就需要用关键字import来导入类。
import access.mypackage.*;
public class Test {
public static void main(String[] args) {
MyClass class = new MyClass();
}
}
6.1.2 创建独一无二的包名
为了避免多个包的文件在同一个目录下混杂,一中合乎逻辑的做法就是将特定的包的所有.class文件都放置于一个目录下。利用操作系统的层次化的文件结构来解决这一问题。
将所有的文件收入一个子目录还可以解决另外的两个问题:
- 按照惯例,package名称的第一部分是类的创建者的反顺序Internet域名,如果按照管理来命名,package的名称也将是独一无二的,防止了名称冲突的问题。
- 将package名称分解成本机上的一个目录。当Java程序运行并加载需要的.class文件时,就可以很直观的确定出文件所在的目录。
Java解释器的运行过程如下:找出环境变量CLASSPATH(可以通过操作系统来设置),CLASSPATH包含了一个或者多个目录,用作查找.class文件的根目录。从根目录开始,解释器获取包的名称并将每个句点替换成反斜杠,以从CLASSPATH根中产生一个路径名称(例如包名foo.bar.baz就会变成foo\bar\baz或者foo/bar/baz,取决于操作系统)。得到的路径会和CLASSPATH中相对应的目录结合,解释器就在这些目录中查找和需要创建的类名相同的.class文件了。
6.1.3 对使用包的告示
无论何时创建包,都已经在给定包名称的时候隐含的指定了目录结构。这个包必须位于其命名所指定的目录之中,而该目录必须是在CLASSPATH开始的目录中可以查询到的。注意,编译过的代码通常放置在和源代码不同的目录中,但必须保证JVN使用CLASSPATH能找到该路径。
6.2 Java访问权限关键词
public、protected、private这几个Java访问权限修饰词在使用的时候,是放在类中每个成员的定义之前,无论这个成员是一个方法还是变量。如果不提供任何形式访问权限修饰,则意味着它是”包(package)访问权限”。
6.2.1 包(package)访问权限
类中的默认访问权限没有任何的关键字,但通常指的是包访问权限(有时也表示称friendly)。这就意味着当前包中的所有其他类对该成员都有访问的权限,但对这个包之外的所有类,却表现出private的特性。
包访问权限允许将包内所有相关的类组合起来,以使它们可以轻松的相互作用。当把类组织起来放进一个包内时,也就给他们的报访问权限的成员赋予了相互访问的权限。
6.2.2 public:接口访问权限
使用关键字public,也就意味着后面紧跟着的成员无论对谁都可直接访问,假设定义了一个包含下面编译单元的dessert包:
package access.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
void bite() {
System.out.println("bite");
}
}
现在如果创建了一个使用Cookie类的程序:
import access.dessert.*;
public class Dinner {
public static void main(String[] args) {
Cookie x = new Cookie();
}
}
这样就可以创建一个Cookie对象了,但是由于bite方法是包内访问权限,不在一个包内,故无法访问该方法。
默认包:如果对两个类都没有用package关键字指明所处的包,则会默认该文件你属于该目录的默认包中,如果这两个类在同一个子目录下,则是可以互相访问包访问权限的成员的。如果上面的例子中两个类文件处于同一目录下,则可以调用x.bite()
方法,否则会报错。
6.2.3 private:无法访问
关键字private的意思是,除了包含该成员的类之外,其他任何类都无法访问这个成员。下面的例子使用了private关键字:
class Sundae{
private Sudae() {
static Sudae makeASudae(){
return new Sudae();
}
}
public class IceCream {
public static void main(String[] args) {
Sundae x = Sundae.makeSundae();
}
}
这个例子阻止外界直接接触构造器,而用作者提供的构造器来创建类的实例。
任何可以肯定只是该类的一个”助手”的方法,都可以指定成private,以确保不会再包内其他地方误用。
6.2.4 protected:继承访问权限
关键字protected处理的是继承的概念,通过继承可以利用一个基类,然后将新成员添加到现有类中而不用碰该现有类。还可以改变该类现有成员的行为。
如果创建了一个新包,并继承来自另外一个包的基类,则唯一能访问的成员就是基类中的public成员。基类的创建者会希望某个特定的成员,把它的访问权限赋予它的子类而不是所有类,这就需要用到protected关键字。protected也提供包访问权限,也就是说是加强版的默认访问权限,除了同一包里的类可以访问之外,还允许继承它的类访问。下面的例子:
//Cookie.java
package access.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
void bite() {
System.out.println("bite");
}
}
目前对方法bite()有访问权限的只有同一包下的。
如果再创建一个类继承Cookie,并将bite()方法的访问权限改为protected
。
//SmallCookie.java
package dada.life
public class SmallCookie extends Cookie{
public SmallCookie() {
System.out.println("SmallCookie constructor"):
}
public void smallBite() {
bite();
}
public static void main() {
SmallCookie sc = new SmallCookie();
sc.smallBite();
}
}
这时候SmallCookie对Cookie中的bite()方法就具有了访问权限。
6.4 类的访问权限
Java中,访问权限修饰词也可以用于确定库中的哪些类对于使用者时可用的。
为了控制某个类的访问权限,修饰词必须出现在关键字class之前。例如public class Widget{}
,然而在使用关键字限制访问权限的时候还有些注意事项:
- 每个编译单元(文件)都只能有一个public类。每个编译单元都有单一的公共接口,用public来修饰。
- 被public修饰的类必须和编译单元(文件)同名,包括大小写。
请注意,类的访问权限既不可以是private(这样会使其他的任何类都无法访问它)也不能是protected的。所以对类的访问权限只有两个选择:包访问权限或者public。如果不希望其他任何人对该类拥有访问权限,可以把所有构造器都设置为private。
如果没能为类访问权限指定一个访问修饰符,它就会默认的得到包访问权限。这就意味着该类的对象可以由包内的任何其他类来创建,但是在包外则不行。(在类的最开头,由package指定的包名相同则视为同一包下,还需要注意默认包名~)。