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