杰瑞科技汇

Java中protected继承权限到底如何使用?

protected 的核心定义

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

Java中protected继承权限到底如何使用?-图1
(图片来源网络,侵删)

当一个成员(字段或方法)被声明为 protected 时,它的访问权限如下:

  1. 在同一个类内部:可以访问。
  2. 在同一个包下的其他类中:可以访问。
  3. 在不同包下的子类中可以访问,这是 protected 最关键的特点,也是我们讨论的重点。
  4. 在不同包下且非子类的类中不可以访问

protected 成员就像是家族的“内部秘密”,家族成员(同一个类、同一个包)都知道,并且可以传承给后代(子类),但外部的、没有血缘关系的家族(不同包的非子类)则无权知晓。


protected 在继承中的具体表现

protected 的主要目的就是为了实现继承,让子类能够访问父类的非 public 成员,同时又比 default(包私有)更安全,因为它限制了跨包的访问范围。

我们通过两个例子来对比:一个在同一个包内的继承,另一个在不同包的继承。

Java中protected继承权限到底如何使用?-图2
(图片来源网络,侵删)

示例 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); // 编译错误!
    }
}

分析:

Java中protected继承权限到底如何使用?-图3
(图片来源网络,侵删)
  • Manager 继承自 Employee,并且它们在同一个 mycompany.employee 包下。
  • ManagermanageTeam 方法可以直接访问父类 Employeeprotected 成员 salary,这是完全合法的。
  • Manager 无法访问 Employeeprivate 成员 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,但它们在不同的包下。
  • StartupstartupLife 方法仍然可以访问父类 Employeeprotected 成员 salary,这正是 protected 关键字的威力所在。
  • 同样,它也无法访问 private 成员 name

与其他访问修饰符的对比表

为了更清晰地理解 protected 的位置,这里有一个对比表,假设 ClassA 是父类,ClassB 是不同包下的子类。

修饰符 同一类中 同一包中 不同包的子类 不同包的非子类
public ✔️ ✔️ ✔️ ✔️
protected ✔️ ✔️ ✔️
default (无修饰符) ✔️ ✔️
private ✔️

最佳实践和注意事项

  1. 何时使用 protected

    • 当你希望一个成员(通常是方法或字段)能够被子类继承和重写/访问,但又不想暴露给整个包(default 的范围)或全世界(public 的范围)时,protected 是最佳选择。
    • 经典场景:模板方法模式,父类定义一个算法的骨架(public 方法),其中一些步骤的具体实现由 protected 方法完成,子类可以重写这些 protected 方法来改变算法的特定部分。
  2. protected 成员不是 API 的一部分

    • 虽然子类可以访问,但 protected 成员通常不被视为公开的 API(Application Programming Interface),因为它们的设计初衷是给“内部”的子类使用的,未来如果修改或删除这些成员,对子类的影响会很大,在设计库或框架时,要谨慎决定哪些成员设为 protected
  3. 构造方法可以是 protected 吗?

    • 可以!这是一个非常巧妙的设计模式。
    • 如果一个类的构造方法是 protected,意味着:
      • 同一个包内的类可以创建它的实例。
      • 不同包下的子类可以在自己的构造方法中通过 super() 调用它来创建实例。
    • 应用场景:当你想限制一个类的实例化方式,只允许它自己、同包类或者它的子类来创建实例时。java.lang.Objectclone() 方法就与这个概念相关,虽然它的构造方法是 public 的,但 clone() 的行为受保护。

  • 核心作用protected 是为继承而生的访问修饰符。
  • 访问权限:允许子类(无论是否在同一个包)访问父类的成员,同时也允许同一个包内的其他类访问。
  • default 的关键区别default(包私有)不允许不同包的子类访问,而 protected 允许。
  • 封装性:它比 public 封装性更好,比 privatedefault 提供了更灵活的继承支持,是封装和继承之间一个很好的平衡点。

掌握 protected 是迈向高级 Java 编程的重要一步,它体现了面向对象设计中关于“封装”和“继承”的深刻权衡。

分享:
扫描分享到社交APP
上一篇
下一篇