public、protected和private这几个java访问权限修饰词在使用时,是置于类中每个成员的定义之前的--无论它是一个域还是一个方法。每个访问权限修饰词仅控制它所修饰的特定定义的访问权。如果不提供任何访问权限修饰词,则意味着它是“包访问权限”。因此,无论如何,所有事物都具有某种形式的访问权限控制。
一、包访问权限
默认访问权限没有任何关键字,但通常是指包访问权限(有时也表示成为friendly)。这就意味着当前的包中的所有其他类对那个成员都有访问权限,但对于这个包之外的所有类,这个成员却是private。由于一个编译单元(即一个文件),只能隶属于一个包,所以经由包访问权限,处于同一个编译单元中的所有类彼此之间都是自动可访问的。
包访问权限允许将包内所有相关的类组合起来,以使它们彼此之间可以轻松地相互作用。当把类组织起来放进一个包内之时,也就给它们的包访问权限的成员赋予了相互访问的权限,你“拥有”了该包内的程序代码。“只有你拥有的程序代码才可以访问你所拥有的其他程序代码”,这是合理的。应该说,包访问权限为把类群聚在一个包中的做法提供了意义和理由。在许多语言中,在文件内组织定义的方式是任意的,但在java中,则要强调你以一种合理的方式对它们加以组织。另外,你可能还想要排除这样的类--它们不应该访问在当前包中所定义的类。
类控制着哪些代码有权访问自己的成员。其他包内的类不能刚一上来就说:“嗨,我是Bob的朋友。”并且还想看到Bob的protected、包访问权限和private成员。取得对某成员的访问权唯一途径是:
- 使该成员成为public。于是,无论是谁,无论在哪里,都可以访问该成员。
- 通过不加访问权限修饰词并将其他类放置于同一个包内的方式给成员赋予包访问权。于是包内的其他类也就可以访问该成员了。
- 继承而来的类既可以访问public成员也可以访问protected成员(但访问private成员却不行)。只有在两个类都处于同一个包内时,它才可以包访问权限的成员。
- 提供访问器(accessor)和变异器(mutator)方法(也被称为get/set方法),以读取和改变数值。对OOP而言,这是最优雅的方式,而且这也是JavaBeans的基本原理。
二、public:接口访问权限
使用关键字public,这就意味着public之后紧跟着的成员声明自己对每个人都是可用的,尤其是使用类库的客户程序员更是如此。假设定义了一个包含下面编译单元的dessert包:
package access.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
void bite() {
System.out.println("bite");
}
}
记住,Cookie.java文件必须位于名为dessert的子目录之中,该子目录在access下。现在如果创建了一个使用Cookie的程序:
import access.dessert.Cookie;
public class Dinner {
public static void main(String[] args) {
Cookie x = new Cookie();
// x.bite();
}
}
就可以创建一个Cookie对象,因为它的构造器是public而且类也是public的。但是,由于bite()只向在dessert包中的类提供访问权,所以bite()成员在Dinner.java之中是无法访问的,因此编译器也禁止你使用它。
三、private:你无法访问
关键字private的意思是,除了包含该成员的类之外,其他任何类都无法访问这个成员。由于处于同一个包内的其他类是不可以访问private成员的,因此这等于说是自己隔离了自己。从另一方面说,让许多人共同合作来创建一个包也是不大可能的,为此private就允许你随意改变该成员,而不必考虑这样做是否会影响到包内其他的类。
默认的包访问权限通常已经提供了充足的隐藏措施。请记住,使用类的客户端程序,是无法访问包访问权限成员的。这样做很好,因为默认访问权限是一种我们常用的权限,同时也是一个种在忘记添加任何访问权限控制时能够自动得到的权限。因此,通常考虑的是,哪些成员是想要明确公开给客户端程序员使用的,从而将它们声明为public,而在最初,你可能不会认为自己经常会需要使用关键字private,因为没有它,照样可以工作。然而,事实很快就会证明,对private的使用是多么的重要,在多线程环境下更是如此。
此处有一个使用private的示例:
class Sundae {
private Sundae() {
}
static Sundae makeASundae() {
return new Sundae();
}
}
public class IceCream {
public static void main(String[] args) {
// Sundae x = new Sundae();
Sundae x = Sundae.makeASundae();
}
}
这是一个说明private终有其用武之地的示例:可能想控制如何创建对象,并阻止别人直接访问某个特定的构造器(或全部构造器)。在上面的例子中,不能通过构造器来创建Sundae对象,而必须调用makeASundae()方法来达到此目的。
任何可以肯定只是该类的一个“助手”方法的方法,都可以把它指定为private,以确保不会在包内的其他地方误用到它,于是也就防止了你会去改变或删除这个方法。将方法指定为private确保了你拥有这种选择权。
这对于类中的private域同样适用。除非必须公开底层实现细目(此种境况很少见),否则就应该将所有的域指定为private。然而,不能因为在类中某个对象的引用是private,就认为其他的对象无法拥有该对象的public引用。
四、protected:继承访问权限
关键字protected处理的是继承的概念,通过继承可以利用一个现有类--我们将其称为基类,然后将新成员添加到该现有类中而不必碰该现有类。还可以改变该类的现有成员的行为。为了从现有类中继承,需要声明新类extends(扩展)了一个现有类,就像这样:
class Foo extends Bar{
类定义中的其他部分看起来都是一样的。
如果创建了一个新包,并自另一个包中继承类,那么唯一可以访问的成员就是源包的public成员。(当然,如果在同一个包内执行继承工作,就可以操纵所有的拥有包访问权限的成员。)有时,基类的创建者会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就需要protected来完成这一工作。protected也提供包访问权限,也就是说,相同包内的其他类可以访问protected元素。
回顾一下先前的例子Cookie.java,就可以得知下面的类是不可以调用拥有包访问权限的成员bite()的:
import access.dessert.Cookie;
public class ChocolateChip extends Cookie {
public ChocolateChip() {
System.out.println("ChocolateChip constructor");
}
public void chomp() {
// bite();
}
public static void main(String[] args) {
ChocolateChip x = new ChocolateChip();
x.chomp();
}
}
有关继承技术的一个很有趣的事情是,如果类Cookie中存在一个方法bite()的话,那么该方法同时也存在于任何一个从Cookie继承而来的类中。但是由于bite()有包访问权限而且它位于另一个包内,所以我们在这个包内是无法使用它的。当然,也可以把它指定为public,但是这样做所有的人就都有了访问权限,而且很可能这并不是你所希望的。如果我们将类Cookie像这样加以更改:
package access.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
protected void bite() {
System.out.println("bite");
}
}
现在bite()对于所有继承自Cookie的类而言,也是可以使用的。
import access.dessert.Cookie;
public class ChocolateChip extends Cookie {
public ChocolateChip() {
System.out.println("ChocolateChip constructor");
}
public void chomp() {
// protected method
bite();
}
public static void main(String[] args) {
ChocolateChip x = new ChocolateChip();
x.chomp();
}
}
注意,尽管bite()也具有包访问权限,但是它仍旧不是public的。
如果本文对您有很大的帮助,还请点赞关注一下。