Java 中的 Protected 修饰符

概述

  • protected 方法的调用是否合法(编译是否通过),关键是要看 调用者所在的类与被调用的 protected 方法所在的类是否在相同的包下,若相同,则合法;否则,不合法。
  • 在子类内部,任何情况下都可以访问父类的 protected 方法

栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// pacakge p1;
public class Father1 {
protected void f() {}
}

// package p1;
public class Son1 extends Father1 {}

// package p2;
public class Son2 extends Father1 {}

// package p1;
public class Test1 {
public static void main(String[] args) {
Son1 son1 = new Son1();
son1.f(); // Compile OK.

Son2 son2 = new Son2();
son2.f(); // Compile OK.
}
}

// package p2;
public class Test2 {
public static void main(String[] args) {
Son1 son1 = new Son1();
son1.f(); // Compile Error.

Son2 son2 = new Son2();
son2.f(); // Compile Error.
}
}

更有趣的栗子

### $\text{栗}_1$
1
2
3
4
5
6
7
8
9
10
11
12
13
// package p1;
public class MyObject1_2 {}

// package p1;
public class Test1_2 {
public static void main(String[] args) {
MyObject1_2 myObject1_2 = new MyObject1_2();
myObject12.clone(); // Compile Error.

Test1_2 test1_2 = new Test1_2();
test1_2.clone(); // Compile OK.
}
}
这里 `myObject1_2.clone` 是继承自 `Object` 的 `protected` 方法,虽然 `Test1_2` 也是 `Object` 的子类,但是 `Test1_2` 和 `MyObject1_2` 并不存在直接的继承关系,并且由于 `Object` 和 `Test1_2` 不在同一个包中,因此编译不通过。 而由于 `Test1_2` 继承自 `Object`,因此可在 `Test1_2` 内部任意调用从 `Object` 继承来的 `protected` 方法。 ### $\text{栗}_2$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// package p1;
public class MyObject1_3 {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

//package p2;
class MyObject2_3 extends MyObject1_3 {}

// package p2;
public class Test2_3 extends MyObject1_3 {
public static void main(String[] args) throws CloneNotSupportedException {
MyObject1_3 myObject1_3 = new MyObject1_3();
myObject1_3.clone(); // Compile Error.

MyObject2_3 myObject2_3 = new MyObject2_3();
myObject2_3.clone(); // Compile Error.


Test2_3 test2_3 = new Test2_3();
test2_3.clone(); // Compile OK.
}
}
和 ***[$\text{栗}_1$](#example_1)*** 中不同,`Test2_3` 继承自 `MyObject1_3`,**因此 `main` 中调用的 `clone` 都可追溯到 `MyObject1_3` 所在的包 `p1`**,以此为前提,很容易分析: 1. `myObject1_3.clone()`: > 因为 `Test2_3` 在包 `p2` 下,和 `clone` 所在的包不同,因此编译不通过。 2. `myObject2_3.clone()`: > 虽然 `MyObject2_3` 和 `Test2_3` 在同一个包下,但由 **[前提](#tmp_asd1)** 很容易分析出编译不通过的原因。 3. `test2_3.clone()`: > 在 `Test2_3` 内部调用继承来的 `protected` 方法都是合法的。 ### $\text{栗}_3$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// package p1;
public class MyObject1_3 {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

// package p1;
public class Test1_3 extends MyObject1_3 {
public static void main(String[] args) throws CloneNotSupportedException {
MyObject1_3 myObject1_3 = new MyObject1_3();
myObject1_3.clone(); // Compile OK.

Test1_3 test1_3 = new Test2_3();
test1_3.clone(); // Compile OK.
}
}
与 ***[$\text{栗}_2$](#example_2)*** 中不同,`MyObject1_3` 和 `Test1_3` 在一个包下,所以可以调用 `MyObject1_3` 的 `protected` 方法。 ### $\text{栗}_4$
1
2
3
4
5
6
7
8
9
10
// package p1;
public class MyObject1_4 extends Test2_4 {}

// package p2;
public class Test2_4 {
public static void main(String[] args) throws CloneNotSupportedException {
MyObject1_4 myObject1_4 = new MyObject1_4();
myObject1_4.clone(); // Compile OK.
}
}
因为 `MyObject1_4` 的 `clone` 继承自 `Test2_4`,所以 `clone` 方法和 `Test2_4` 在相同的包下,因此编译通过。(注意与 ***[$\text{栗}_1$](#example_1)*** 对比) ### $\text{栗}_5$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// package p1;
public class MyObject1_5 extends Test2_5 {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

// package p2;
public class Test2_5 {
public static void main(String[] args) {
MyObject1_5 myObject1_5 = new MyObject1_5();
myObject1_5.clone(); // Compile Error.
}
}
与 ***[$\text{栗}_4$](#example_4)*** 不同,`MyObject1_5` 覆盖了父类的 `clone` 方法,因此在调用 `MyObject1_5` 的 `clone` 方法时,实际上是被调用的方法所在的包为 `p1`,而 `Test2_5` 所在的包为 `p2`,因此编译不通过。

Hint

参考链接