MST

星途 面试题库

面试题:Java类可见性在继承与多态中的复杂应用

假设有一个Java项目,存在多层继承关系,基类使用protected修饰部分方法和成员变量。在不同包中有多个子类继承该基类,并对这些受保护成员进行重写或访问。请分析在这种情况下,不同访问修饰符对类的可见性以及方法重写规则会如何相互作用,导致哪些可能出现的问题及解决方案。
47.0万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

不同访问修饰符对类的可见性影响

  1. 基类的protected成员
    • 在Java中,protected修饰的成员变量和方法,对于同包中的所有类可见,对于不同包中的子类也可见。所以在不同包中的子类可以访问和重写基类的protected成员。
  2. 子类对protected成员的重写
    • 当子类重写基类的protected方法时,子类方法的访问修饰符不能比基类更严格。即如果基类方法是protected,子类重写方法可以是protected或者public,但不能是private。例如:
class Base {
    protected void method() {
        // 方法实现
    }
}

class Sub extends Base {
    @Override
    public void method() {
        // 重写实现
    }
}
  1. 类的可见性与继承
    • 如果基类是public,所有包中的类都可以继承它。如果基类是default(包访问权限,无修饰符),只有同包中的类可以继承它。这会间接影响到protected成员的访问,因为只有能继承基类的子类才能访问其protected成员。

可能出现的问题

  1. 访问权限冲突
    • 当子类重写方法时,如果不小心将访问修饰符设置得比基类更严格,会导致编译错误。例如:
class Base {
    protected void method() {
        // 方法实现
    }
}

class Sub extends Base {
    @Override
    private void method() { // 编译错误,访问权限比基类更严格
        // 重写实现
    }
}
  1. 隐藏与重写混淆
    • 在继承关系中,如果子类定义了与基类protected方法同名但参数列表不同的方法,这不是重写而是隐藏。这可能导致意外的行为,因为隐藏方法和重写方法的调用机制不同。例如:
class Base {
    protected void method(int i) {
        System.out.println("Base method with int param");
    }
}

class Sub extends Base {
    protected void method(String s) { // 这是隐藏,不是重写
        System.out.println("Sub method with String param");
    }
}
  1. 包访问混乱
    • 由于protected成员对同包类和不同包子类可见,可能会导致在同包中的非子类意外访问到protected成员,从而破坏类的封装性。例如:
package com.example;
class Base {
    protected int value;
}

package com.example;
class NonSub {
    public void accessBase() {
        Base base = new Base();
        base.value = 10; // 同包非子类可以访问protected成员
    }
}

解决方案

  1. 仔细检查重写方法的访问权限
    • 在重写protected方法时,确保子类方法的访问修饰符符合规则,要么与基类相同(protected),要么更宽松(public)。
  2. 明确方法重写意图
    • 为了避免隐藏与重写混淆,在重写方法时使用@Override注解。如果不小心写成隐藏方法,编译器会报错。例如:
class Base {
    protected void method(int i) {
        System.out.println("Base method with int param");
    }
}

class Sub extends Base {
    @Override // 如果方法签名不匹配,编译器会报错
    protected void method(int i) {
        System.out.println("Sub method with int param");
    }
}
  1. 强化封装性
    • 如果不希望同包中的非子类访问protected成员,可以考虑将相关逻辑封装在内部方法中,通过公开方法间接访问。或者将protected成员改为private,通过公开的访问器方法访问。例如:
class Base {
    private int value;
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
}