Java的访问修饰符用于控制类、方法、变量等成员的访问范围,其中protected修饰符的使用规则相对复杂,很多开发者在实际开发中容易出现理解偏差。它既和包访问权限有关,也和继承关系绑定,需要结合具体场景判断访问合法性。

protected修饰符的基础规则
protected修饰的成员具有以下访问权限:
- 可以被同一个包内的其他类直接访问
- 可以被不同包下的子类访问,但访问方式存在限制
- 不能被不同包下无继承关系的类访问
同包场景下的使用
当类和目标成员在同一个包中时,protected成员的访问规则和默认访问权限(不写修饰符)一致,不需要考虑继承关系。
例如我们定义同一个包com.demo下的两个类:
package com.demo;
public class Parent {
protected String name = "父类名称";
protected void printInfo() {
System.out.println("父类方法");
}
}
package com.demo;
public class SamePackageClass {
public void test() {
Parent parent = new Parent();
// 同包下可以直接访问protected成员
System.out.println(parent.name);
parent.printInfo();
}
}
不同包子类场景下的使用
不同包的子类访问父类的protected成员时,有严格的限制:只能通过子类自身的实例、或者子类实例的继承链上的父类实例访问,不能通过父类的独立实例访问。
首先定义父类的包为com.base:
package com.base;
public class BaseParent {
protected int age = 10;
protected void showAge() {
System.out.println("年龄:" + age);
}
}
然后在不同包com.child下定义子类:
package com.child;
import com.base.BaseParent;
public class ChildClass extends BaseParent {
public void testAccess() {
// 正确:通过子类自身实例访问继承的protected成员
System.out.println(this.age);
this.showAge();
// 正确:通过父类实例访问(该实例是子类继承链上的)
BaseParent parent = new ChildClass();
System.out.println(parent.age);
parent.showAge();
// 错误:不能通过父类的独立实例访问
BaseParent otherParent = new BaseParent();
// System.out.println(otherParent.age); // 编译报错
// otherParent.showAge(); // 编译报错
}
}
常见误区解析
误区1:认为protected成员可以被任意外部类访问
很多开发者误以为protected是“半公开”的权限,只要不是private就可以随便访问,实际上不同包下无继承关系的类完全无法访问protected成员,和private的限制差异仅体现在同包和继承场景。
误区2:不同包子类可以通过父类实例访问protected成员
这是最常见的错误,如上面的代码示例所示,不同包的子类只能通过自身实例或者子类化的父类实例访问protected成员,直接创建父类的独立实例并访问其protected成员会直接编译失败。
误区3:protected修饰的构造方法可以被任意子类调用
protected修饰的构造方法确实可以被子类调用,但同样遵循继承规则,不同包的子类调用父类protected构造方法时,需要通过super关键字,且不能在其他无关类中通过new父类实例调用。
package com.base;
public class ParentWithProtectedConstructor {
protected ParentWithProtectedConstructor(String param) {
System.out.println("构造参数:" + param);
}
}
package com.child;
import com.base.ParentWithProtectedConstructor;
public class Child extends ParentWithProtectedConstructor {
public Child() {
// 正确:子类通过super调用父类protected构造方法
super("子类调用");
}
}
使用建议
在实际开发中,如果需要让成员仅对同包类和子类开放,就可以使用protected修饰符。如果不需要继承场景下的访问,优先考虑使用默认访问权限或者private权限,避免过度暴露成员导致代码耦合度升高。同时在使用protected修饰方法时,要注意子类重写该方法时不能降低访问权限,否则会违反Java的重写规则。