protected是java四种访问指示符中的一种,它表明被它修饰的成员为保护类型。要求小写。


   protected关键字修饰的成员是字段变量和方法变量。当protected修饰这两种成员时,他们只能被同一个包里的类和该类的子类所访问(子类可以和父类不在一个包中)。

所以,使用的protected的时机是当你想让一个类中的某个方法或成员变量在包中都可见,而且其子类也能访问(子类有可能和父类不在同一个包中)但又不想让所有类都可以访问该类时,就可以用protected修饰符。


    protected 关键字还为我们引入了一种名为“继承”的概念,它以现有的类为基础,并在其中加入新的成员,同时不会对现有的类产生影响——我们将这种现有的类称为“基础类”或者“基本类”(Base Class)。

   对于从一个现有类的继承,我们说自己的新类“扩展”(extends)了那个现有的类。继承的类定义开头部分如下所示:

   class Foo extends Bar {

   类定义剩余的部分看起来是完全相同的。

若新建一个包,并从另一个包内的某个类里继承,则唯一能够访问的成员就是原来那个包的 public 成员。当然,如果在相同的包里进行继承,那么继承获得的包能够访问所有“友好”的成员。例如:


//: Cookie.java

// Creates a library

package c05.dessert;

public class Cookie {

public Cookie() {

System.out.println("Cookie constructor");

}

void foo() { System.out.println("foo"); }

} ///:~


则下面这个类就不能访问“友好”的成员:

//: ChocolateChip.java

// Can't access friendly member

// in another class

import c05.dessert.*;

public class ChocolateChip extends Cookie {

public ChocolateChip() {

System.out.println(

"ChocolateChipconstructor");

}

public static void main(String[] args) {

ChocolateChip x = new ChocolateChip();

//! x.foo(); // Can't access foo

}

} ///:~


   对于继承,值得注意的一件有趣的事情是倘若方法 foo()存在于类 Cookie 中,那么它也会存在于从 Cookie继承的所有类中。但由于 foo()在外部的包里是“友好”的,所以我们不能使用它。当然,亦可将其变成public。但这样一来,由于所有人都能自由访问它,所以可能并非我们所希望的局面。若象下面这样修改类:


Cookie:

public class Cookie {

public Cookie() {

System.out.println("Cookie constructor");

}

protected void foo() {

System.out.println("foo");

}

}


   那么仍然能在包 dessert 里“友好”地访问 foo(),但从 Cookie 继承的其他东西亦可自由地访问它。然而,它并非公共的(public)。


   下面用另外一个例子进一步阐述继承的概念:


   我们将创建一个父类Bird.java,放在birdpack包中,父类中有一个protected int的成员变量nFeathers;

   再分别创建4个Bird类的子类Duck1.java,Duck2.java,Duck3.java,Swan.java,放在duckpack包中,通过在每个子类中调用nFeathers的不同方法说明上述几点

   下面的程序并不用于运行,因为访问控制在编译期间就要确定,我们只需编译下述文件,看是否能通过。在编译下述文件前,先想想能不能编译通过?

1 //Bird.java------------------------------  

2 package birdpack;  

3  

4 public class Bird{  

5  protected int nFeathers;  

6    

7 }  

8  

9 //Duck1.java-----------------------------  

10 package duckpack;  

11  

12 import birdpack.Bird;  

13  

14 public class Duck1 extends Bird{  

15  public void setn(int duck1n){  

16   //在子类中直接访问父类中的protected变量  

17   nFeathers=duck1n;  

18  }  

19 }  

20 //Duck2.java------------------------------    

21 package duckpack;  

22  

23 import birdpack.Bird;  

24  

25 public class Duck2 extends Bird{  

26  

27  public void construct(int newduck2){  

28   Duck2 d2 = new Duck2();  

29   //在子类中通过子类的对象访问父类中的protected变量  

30   d2.nFeathers=newduck2;  

31   Bird d = new Duck2();  

32   d2.nFeathers=newduck2;//(编译错误)  

33   //父类引用指向之类对象是无法调用protected变量的。  

34  }  

35 }  

36  

37 //Duck3.java------------------------------  

38 package duckpack;  

39  

40 import birdpack.Bird;  

41  

42 public class Duck3 extends Bird{  

43  

44  public void construct(int newduck3){  

45   Bird b = new Bird();  

46   //子类中用父类对象反而不能访问父类中的protected变量  

47   b.nFeathers=newduck3;  

48  }  

49 }  

50  

51 //Swan.java--------------------------------  

52 package duckpack;  

53  

54 import birdpack.Bird;  

55  

56 public class Swan extends Bird{  

57  

58  public void construct(int swan){  

59   Duck1 d1 = new Duck1();  

60   //子类中用另外一个子类的对象也不能访问父类中的protected变量

61   d1.nFeathers=swan;  

62  }  

63 }  

    编译上述几个文件,后2个不能通过。编译器提示:


    " nFeathers has protected access in birdpack.Bird"。


    但是,如果子类Duck1重写了protected变量或方法,那么Swan.java中d1对象可以访问该变量或方法。

    在Duck3和Swan两个子类中,直接通过父类和另一个子类来访问父类中的protected方法和成员变量就不行。


   protected 修饰的成员变量和“友好”的成员变量区别在于:在包外的子类可以继承protected成员变量,而且被继承的protected的成员变量,在子类中仍然是protected(如果方法没有被override),但是要注意的是,我这里说它们仍然是protected,是从它们可以由该子类的包外的子类继续继承的递归性角度来说的,实际上它们的可见范围和该子类中新定义的protected 成员变量是有区别的。不同之处在于在该子类中新定义的protected 成员变量对该子类所在的包是可见的。而从父类中继承的protected 成员变量在该子类所在的包中仅仅对该子类是可见的,同时另外它们还享有被继承前的可见范围(即被被继承前的可见范围仍然保持。这让人想起oop中的一个原则,成员变量被继承后,其可见的范围只能扩大,不能缩小)。


   对于构造函数,protected修饰词带给它的语义本质上和带给其他方法的是一样的。但因为构造函数相比一般的方法有特别之处,所以protected语义在具体体现上也会有些不同。比如构造函数不存在继承的问题,但是构造函数有一个隐含或显式super()调用的问题。如果您对protected语义有了本质的认识,您一定能想到,你在包外的任何地方你都不能用new的方式直接调用某类的protected构造函数,但是在该类的包外的子类的构造函数中,是可以隐含地调用前者的protected构造函数的,也可以显式的通过super()调用(这个不难理解,就像他的其他protected方法可以用super.的方式来调用一样)。另外提醒下,构造函数从来都不是继承得来的,构造函数的可见性和父类的构造函数的可见性没有什么必然联系。