protected 的核心定义
protected 是 Java 四种访问修饰符(public, protected, default/package-private, private)中的一种。

当一个成员(字段或方法)被声明为 protected 时,它的访问权限如下:
- 在同一个类内部:可以访问。
- 在同一个包下的其他类中:可以访问。
- 在不同包下的子类中:可以访问,这是
protected最关键的特点,也是我们讨论的重点。 - 在不同包下且非子类的类中:不可以访问。
protected 成员就像是家族的“内部秘密”,家族成员(同一个类、同一个包)都知道,并且可以传承给后代(子类),但外部的、没有血缘关系的家族(不同包的非子类)则无权知晓。
protected 在继承中的具体表现
protected 的主要目的就是为了实现继承,让子类能够访问父类的非 public 成员,同时又比 default(包私有)更安全,因为它限制了跨包的访问范围。
我们通过两个例子来对比:一个在同一个包内的继承,另一个在不同包的继承。

示例 1:同一个包内的继承
项目结构:
mycompany/
└── employee/
├── Employee.java (父类)
└── Manager.java (子类)
Employee.java (父类)
package mycompany.employee;
public class Employee {
private String name; // private,只有 Employee 自己能访问
protected double salary; // protected,子类和同包类可以访问
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
// public 方法,可以被任何人访问
public void displayInfo() {
System.out.println("Name: " + this.name);
System.out.println("Salary: " + this.salary);
}
}
Manager.java (子类)
package mycompany.employee; // 注意:在同一个包下
public class Manager extends Employee {
private String department;
public Manager(String name, double salary, String department) {
super(name, salary); // 调用父类的构造方法
this.department = department;
}
// 子类自己的方法
public void manageTeam() {
// 可以直接访问父类的 protected 成员 salary
System.out.println(this.department + " department manager is managing a team with a budget based on salary: " + this.salary);
// 不能访问父类的 private 成员 name
// System.out.println("Manager's name is: " + this.name); // 编译错误!
}
}
分析:

Manager继承自Employee,并且它们在同一个mycompany.employee包下。Manager的manageTeam方法可以直接访问父类Employee的protected成员salary,这是完全合法的。Manager无法访问Employee的private成员name,这符合封装原则。
示例 2:不同包下的继承
项目结构:
mycompany/
├── employee/
│ ├── Employee.java
│ └── Manager.java
└── startup/
└── Startup.java
Employee.java (父类,与上面相同)
package mycompany.employee;
public class Employee {
// ... (代码同上)
protected double salary;
// ...
}
Startup.java (子类,在不同包下)
package mycompany.startup; // 注意:在不同的包下
import mycompany.employee.Employee; // 导入父类
public class Startup extends Employee {
public void startupLife() {
// 可以访问父类的 protected 成员 salary
// 因为 Startup 是 Employee 的子类
this.salary = 10000.0;
System.out.println("Startup employee salary is set to: " + this.salary);
// 不能访问父类的 private 成员 name
// System.out.println("Name: " + this.name); // 编译错误!
}
}
分析:
Startup继承自Employee,但它们在不同的包下。Startup的startupLife方法仍然可以访问父类Employee的protected成员salary,这正是protected关键字的威力所在。- 同样,它也无法访问
private成员name。
与其他访问修饰符的对比表
为了更清晰地理解 protected 的位置,这里有一个对比表,假设 ClassA 是父类,ClassB 是不同包下的子类。
| 修饰符 | 同一类中 | 同一包中 | 不同包的子类 | 不同包的非子类 |
|---|---|---|---|---|
public |
✔️ | ✔️ | ✔️ | ✔️ |
protected |
✔️ | ✔️ | ✔️ | ❌ |
default (无修饰符) |
✔️ | ✔️ | ❌ | ❌ |
private |
✔️ | ❌ | ❌ | ❌ |
最佳实践和注意事项
-
何时使用
protected?- 当你希望一个成员(通常是方法或字段)能够被子类继承和重写/访问,但又不想暴露给整个包(
default的范围)或全世界(public的范围)时,protected是最佳选择。 - 经典场景:模板方法模式,父类定义一个算法的骨架(
public方法),其中一些步骤的具体实现由protected方法完成,子类可以重写这些protected方法来改变算法的特定部分。
- 当你希望一个成员(通常是方法或字段)能够被子类继承和重写/访问,但又不想暴露给整个包(
-
protected成员不是 API 的一部分- 虽然子类可以访问,但
protected成员通常不被视为公开的 API(Application Programming Interface),因为它们的设计初衷是给“内部”的子类使用的,未来如果修改或删除这些成员,对子类的影响会很大,在设计库或框架时,要谨慎决定哪些成员设为protected。
- 虽然子类可以访问,但
-
构造方法可以是
protected吗?- 可以!这是一个非常巧妙的设计模式。
- 如果一个类的构造方法是
protected,意味着:- 同一个包内的类可以创建它的实例。
- 不同包下的子类可以在自己的构造方法中通过
super()调用它来创建实例。
- 应用场景:当你想限制一个类的实例化方式,只允许它自己、同包类或者它的子类来创建实例时。
java.lang.Object的clone()方法就与这个概念相关,虽然它的构造方法是public的,但clone()的行为受保护。
- 核心作用:
protected是为继承而生的访问修饰符。 - 访问权限:允许子类(无论是否在同一个包)访问父类的成员,同时也允许同一个包内的其他类访问。
- 与
default的关键区别:default(包私有)不允许不同包的子类访问,而protected允许。 - 封装性:它比
public封装性更好,比private和default提供了更灵活的继承支持,是封装和继承之间一个很好的平衡点。
掌握 protected 是迈向高级 Java 编程的重要一步,它体现了面向对象设计中关于“封装”和“继承”的深刻权衡。
