完整理解protected关键字

来源:互联网 发布:亚马逊 大数据 城市 编辑:程序博客网 时间:2024/05/16 12:19

    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.的方式来调用一样)。另外提醒下,构造函数从来都不是继承得来的,构造函数的可见性和父类的构造函数的可见性没有什么必然联系。

 

原创粉丝点击