protected 不允许跨包子类通过父类实例访问其成员,仅可通过继承关系访问自身或子类实例的protected成员。

protected 关键字在 Java 中**并不允许跨包的子类“直接访问”父类的 protected 成员**——这是最常见的误解。它只允许跨包子类通过**继承关系**(即作为自身成员)访问,而不能通过父类实例引用访问。
为什么 protected 看似“失效”:访问主体决定权限
Java 的 protected 修饰符语义是:对同一包内所有类 + 不同包中**继承该类的子类**开放,但有一个关键限制:子类只能访问“当前对象”(this)或其子类实例中的 protected 成员,不能访问“其他父类实例”的 protected 成员。
常见错误现象:
package p1;
public class Parent {
protected int value = 42;
}
package p2;
import p1.Parent;
public class Child extends Parent {
public void test() {
System.out.println(this.value); // ✅ 编译通过:通过 this 访问
System.out.println(new Child().value); // ✅ 编译通过:Child 实例,属于本类继承链
System.out.println(new Parent().value); // ❌ 编译错误:Cannot resolve symbol 'value'
}
}
原因:虽然 Child 是 Parent 的子类,但 new Parent() 创建的是一个“非本类继承链上的父类实例”,此时 value 对 Child 来说不可见。
跨包子类访问 protected 成员的合法方式
必须满足“访问发生在继承上下文中”,且目标是当前对象或子类对象本身:
- 在子类方法中直接使用
this.field或super.field - 在子类中定义方法返回
this.field,供外部调用(封装后暴露) - 子类重写父类
protected方法,并在其中操作protected字段 - 若需让外部(包括同包/跨包其他类)读取,应在父类中提供
protected或publicgetter,而非暴露字段本身
与 default(包私有)和 public 的关键区别
对比行为更清晰:
-
default:仅限同一包内访问,无论是否继承 -
protected:同一包内任意类可访问;跨包时仅限子类,且仅限访问“本类继承所得”的成员(不能访问其他父类实例) -
public:无限制,但破坏封装性,通常不用于核心数据字段
性能与兼容性无差异——三者都是编译期检查,运行时无额外开销。但误用 protected 暴露字段会导致子类强耦合父类实现细节,一旦父类字段改名或重构,所有跨包子类都会编译失败。
真正需要跨包共享逻辑时,该怎么做?
如果目标是“让不同包的协作类安全使用父类核心能力”,protected 字段不是解法:
- 把逻辑封装进
protected或public方法,而非暴露字段 - 用
public接口 + 默认方法定义契约,由父类实现,子类可选择重写 - 若必须跨包访问状态,考虑引入
public的不可变视图(如public List<t> getItems()</t>返回Collections.unmodifiableList) - 避免在父类中声明
protected字段——这是 Java 社区广泛认为的反模式
最常被忽略的一点:IDE 和静态分析工具(如 IntelliJ、SonarQube)会警告 protected 字段,因为它们本质上打破了封装边界;而 protected 方法则被鼓励用于模板方法模式等受控扩展场景。










