java保护权限怎么调用
- 后端开发
- 2025-08-14
- 38
Java中protected成员可被同包类或子类(含不同包的子类)调用,需通过对象实例访问,如
在Java编程语言中,protected是一种重要的访问控制级别,它介于public(完全公开)和private(完全私有)之间,理解如何正确调用和使用protected成员是掌握面向对象编程的关键之一,以下是对这一主题的全面解析,涵盖定义、使用场景、具体调用规则、示例代码、注意事项以及常见问题解答。
核心概念:什么是protected权限?
protected关键字用于修饰类的成员变量、方法和构造器,其核心特性如下:
- 同包可见性:同一包内的所有类均可直接访问该成员。
- 跨包子类可见性:即使处于不同包,只要是该类的子类(包括直接子类和间接子类),也可以访问此成员。
- 限制条件:若未建立继承关系,则无法通过外部类直接访问其他包中的
protected成员。
关键区别:与
public相比,protected禁止非子类的跨包访问;与default(包私有)相比,它允许跨包的子类访问。
调用规则详解
▶️ 场景1:同一包内的调用
无论是否存在继承关系,同一包内的所有类均可直接访问目标类的protected成员,这是最简单的情况。
示例结构:
src/com/example/Parent.java
src/com/example/Child.java
src/com/example/OtherClass.java
代码演示:

package com.example;
public class Parent {
protected String message = "Hello from Parent"; // protected字段
protected void display() { // protected方法
System.out.println(message);
}
}
class Child extends Parent { // 同一包下的子类
public void callProtected() {
System.out.println(message); // ️ 合法:直接访问protected字段
display(); // ️ 合法:直接调用protected方法
}
}
class OtherClass { // 同一包下的无关类
public void testAccess() {
Parent p = new Parent();
System.out.println(p.message); // ️ 合法:同包可访问protected字段
p.display(); // ️ 合法:同包可调用protected方法
}
}
▶️ 场景2:不同包下的子类调用
当子类位于不同包时,只能通过子类自身的实例或子类内部访问父类的protected成员。不允许通过父类实例直接访问。
示例结构:
src/com/mainpkg/BaseClass.java
src/com/subpkg/DerivedClass.java
代码演示:
// BaseClass.java (com.mainpkg)
package com.mainpkg;
public class BaseClass {
protected int num = 42; // protected字段
protected void printNum() { // protected方法
System.out.println("Value: " + num);
}
}
// DerivedClass.java (com.subpkg)
package com.subpkg;
import com.mainpkg.BaseClass;
public class DerivedClass extends BaseClass { // 不同包的子类
public void accessFromSubclass() {
System.out.println(num); // ️ 合法:子类可直接访问protected字段
printNum(); // ️ 合法:子类可直接调用protected方法
}
public void invalidDirectAccess() {
BaseClass obj = new BaseClass(); // ️ 错误起点!
// System.out.println(obj.num); // 编译错误:无法跨包通过父类实例访问protected成员
// obj.printNum(); // 编译错误
}
}
▶️ 场景3:通过子类暴露给外部
实际开发中常通过子类提供公共接口,间接访问基类的protected成员。
最佳实践示例:

// Manager.java (com.company)
package com.company;
public class Manager {
protected double salary; // protected字段
protected void updateSalary(double amount) { // protected方法
this.salary = amount;
}
}
// DepartmentManager.java (com.hr)
package com.hr;
import com.company.Manager;
public class DepartmentManager extends Manager {
public void setDepartmentBudget(double budget) {
updateSalary(budget); // ️ 合法:子类内部调用protected方法
}
public double getCurrentSalary() {
return salary; // ️ 合法:子类内部访问protected字段
}
}
行为对照表
| 访问来源 | public | default | protected | private |
|---|---|---|---|---|
| 同一类 | ||||
| 同包非子类 | ||||
| 不同包子类 | ||||
| 不同包非子类 | ||||
| 全局可见(任何地方) |
典型错误与解决方案
错误写法1:跨包非子类强行访问
// Test.java (com.test)
import com.mainpkg.BaseClass;
public class Test {
public static void main(String[] args) {
BaseClass obj = new BaseClass();
System.out.println(obj.num); // 编译错误!
}
}
原因:num是protected成员,且Test既不是BaseClass的子类,也不在同一包内。
修正方案:改为子类或移至同包。
错误写法2:忽略继承链要求
// FarAwayClass.java (com.remote)
import com.mainpkg.BaseClass;
public class FarAwayClass extends ThirdPartyIntermediateClass { // 中间层无关联
public void forceAccess() {
BaseClass obj = new BaseClass();
obj.printNum(); // 编译错误!虽然自称"远亲",但无实际继承关系
}
}
原因:仅当存在直接/间接继承关系时,才允许跨包访问protected成员。
高级应用场景
工厂模式中的受控实例化
// Engine.java (com.vehicle)
package com.vehicle;
public class Engine {
protected String modelName; // protected字段
protected Engine(String model) { // protected构造器
this.modelName = model;
}
public static Engine createEngine(String type) {
return new Engine(type); // ️ 本类内部可调用protected构造器
}
}
// CarFactory.java (com.factory)
package com.factory;
import com.vehicle.Engine;
public class CarFactory {
public Engine buildHighPerformanceEngine() {
return new Engine("TurboV8"); // 编译错误!无法跨包调用protected构造器
}
}
解决方案:修改为开放工厂方法或调整构造器可见性。
模板方法模式的设计约束
// GameCharacter.java (com.game)
package com.game;
public abstract class GameCharacter {
protected void moveForward() { // protected模板方法
System.out.println("Moving forward...");
}
public final void performAction() { // 终端模板方法
validateState(); // 钩子方法前奏
moveForward(); // 核心流程(protected)
postProcess(); // 收尾工作
}
protected abstract void validateState(); // 抽象钩子方法
protected abstract void postProcess(); // 抽象钩子方法
}
// Warrior.java (com.game.characters)
package com.game.characters;
import com.game.GameCharacter;
public class Warrior extends GameCharacter {
@Override
protected void validateState() { // 实现抽象钩子方法
System.out.println("Warrior ready!");
}
@Override
protected void postProcess() { // 实现抽象钩子方法
System.out.println("Sword sheathed.");
}
}
此处moveForward()声明为protected,确保只有子类能重写该方法,同时保证模板方法的整体结构不被破坏。

相关问答FAQs
Q1: 为什么在不同包中必须通过子类实例才能访问protected成员?
A: Java的设计哲学强调封装性和可控性。protected的初衷是支持家族式扩展(即继承体系),而非随意跨包访问,这种限制防止了以下风险:
- 意外修改敏感状态(如内部计数器)
- 破坏类的不变式约束
- 暴露不应公开的实现细节
通过强制继承关系,编译器能确保调用者了解类的设计理念,从而更安全地使用受保护的成员。
Q2: 如果子类覆盖了父类的protected方法,能否降低可见性?
A: 根据Liskov替换原则和Java规范,不允许降低可见性。
// 父类(com.base)
protected void oldMethod();
// 子类(com.derived)
@Override public void oldMethod() {} // ️ 合法:提高可见性为public
@Override protected void oldMethod() {} // ️ 合法:保持相同可见性
@Override private void oldMethod() {} // 编译错误!不能降低可见性
此规则确保父类的客户端代码不会因子类缩小访问范围
