为何在包外访问protected成员仅允许使用子类引用?
先看你给出的代码示例(修正了代码里的小笔误):
package pack1; class A{ protected void m1(){ System.out.println("protected modifier"); } } package pack2; class B extends A{ public static void main(String[] args){ B b = new B();//Valid b.m1(); A a = new B();//Invalid a.m1(); A a1 = new A();//InValid a1.m1(); } }
你提到的这个问题是Java访问控制规则里很容易混淆的点,我来拆解一下背后的设计逻辑:
核心设计意图:protected是给子类"继承扩展"用的,不是给外部代码"跨包调用父类"用的
Java的protected权限本质是在封装性和继承扩展性之间做的平衡:它既不像private那样完全封闭,也不像public那样完全开放,而是专门给子类开了一个口子,让子类能复用父类的功能,但又要防止外部代码随便访问父类的内部成员。
1. 为什么子类引用(B b = new B())调用合法?
当你用子类类型的引用时,编译器明确知道这是子类的实例。对于子类B来说,它继承了父类A的m1()方法,这个方法在B的语境里就相当于自己的成员(只是继承来的),所以你作为子类的使用者,访问这个继承来的方法完全符合protected的设计初衷——子类有权使用父类的成员来完成自己的功能。
2. 为什么父类引用指向子类对象(A a = new B())不合法?
这里的关键是编译器只看引用的静态类型,不追踪实际的运行时类型。当你用A类型的引用时,编译器会按照A的访问规则来检查:对于pack2包的代码来说,A的protected成员只能被A的子类在自身的实例上访问,而不是通过任意A类型的引用访问。
如果允许这种情况,那protected的限制就形同虚设了——任何包外的代码只要把子类对象转成父类引用,就能调用protected方法,这完全违背了Java设计protected权限的初衷。
3. 为什么直接实例化父类(A a1 = new A())不合法?
这是最直接的访问控制限制:pack2包和pack1包是不同的包,对于pack2的代码来说,A的protected成员不属于"同包访问",也不属于"子类继承访问"(你是直接创建A的实例,不是作为子类来访问),所以自然没有权限调用。
再补充一下Java protected权限的完整规则,帮你彻底理清:
- 同包内的所有类,都可以访问该类的protected成员;
- 子类可以访问父类的protected成员,但有两个限制:
- 要么是在子类内部访问自己的实例(比如
this.m1()或者super.m1()); - 要么是在子类内部访问子类实例的该成员(比如你代码里的
b.m1(),b是B类型的实例); - 但子类不能通过父类类型的引用去访问,哪怕这个引用实际指向子类对象。
- 要么是在子类内部访问自己的实例(比如
内容的提问来源于stack exchange,提问作者sainath pawar

