MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java多态下方法重写的访问权限规则

2021-11-051.5k 阅读

Java多态下方法重写的访问权限规则

在Java编程中,多态是一个重要的概念,它允许我们以统一的方式处理不同类型的对象。方法重写是实现多态的关键机制之一,当子类继承父类并提供与父类中方法相同的签名(方法名、参数列表和返回类型)时,就发生了方法重写。在方法重写的过程中,访问权限规则起着至关重要的作用,它确保了代码的安全性和一致性。下面我们将深入探讨Java多态下方法重写的访问权限规则。

访问权限修饰符概述

在开始讨论方法重写的访问权限规则之前,我们先来回顾一下Java中的访问权限修饰符。Java中有四种访问权限修饰符:

  1. public:公共的,被public修饰的成员(类、方法、变量等)可以在任何地方被访问,无论是在同一个包内还是不同的包中。
  2. protected:受保护的,被protected修饰的成员可以在同一个包内被访问,同时在不同包中的子类也可以访问。
  3. default(默认,也称为包访问权限):没有使用任何修饰符时,成员具有默认的包访问权限。这意味着该成员只能在同一个包内被访问。
  4. private:私有的,被private修饰的成员只能在声明它的类内部被访问,其他类,即使是子类,也无法访问。

方法重写的基本概念

方法重写发生在子类中,子类提供了与父类中某个方法相同的方法签名。例如:

class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }
}

在上述代码中,Dog类继承自Animal类,并重写了makeSound方法。通过这种方式,当我们创建Dog类的实例并调用makeSound方法时,会执行Dog类中重写的方法,而不是Animal类中的方法,这就是多态的体现。

方法重写的访问权限规则

  1. 重写方法的访问权限不能比被重写方法的访问权限更严格:这是一个关键的规则。例如,如果父类中的方法是public,那么子类中重写的方法必须也是public,不能是protecteddefaultprivate。因为如果重写方法的访问权限更严格,那么通过父类引用调用该方法时,可能会导致访问失败,破坏了多态的特性。
class Parent {
    public void method() {
        System.out.println("Parent method");
    }
}

class Child extends Parent {
    // 正确,重写方法访问权限与父类相同
    @Override
    public void method() {
        System.out.println("Child method");
    }
}

class AnotherChild extends Parent {
    // 错误,重写方法访问权限比父类更严格
    // @Override
    // protected void method() {
    //     System.out.println("AnotherChild method");
    // }
}
  1. 如果父类中的方法是protected,子类中重写的方法可以是protectedpublic:这是因为protectedpublic的访问范围都涵盖了子类可以访问的范围。
class Base {
    protected void display() {
        System.out.println("Base display");
    }
}

class Derived extends Base {
    // 正确,重写方法访问权限与父类相同
    @Override
    protected void display() {
        System.out.println("Derived display");
    }
}

class AnotherDerived extends Base {
    // 正确,重写方法访问权限比父类更宽松
    @Override
    public void display() {
        System.out.println("AnotherDerived display");
    }
}
  1. 如果父类中的方法具有默认(包访问)权限,子类中重写的方法可以是默认权限、protectedpublic:这是因为默认权限只能在同一个包内访问,而protectedpublic权限可以满足在同一个包内以及不同包中的子类访问的需求。
package com.example.pack1;

class DefaultParent {
    void defaultMethod() {
        System.out.println("DefaultParent defaultMethod");
    }
}

package com.example.pack1;

class DefaultChild extends DefaultParent {
    // 正确,重写方法访问权限与父类相同
    @Override
    void defaultMethod() {
        System.out.println("DefaultChild defaultMethod");
    }
}

package com.example.pack2;
import com.example.pack1.DefaultParent;

class DifferentPackageChild extends DefaultParent {
    // 正确,重写方法访问权限更宽松
    @Override
    public void defaultMethod() {
        System.out.println("DifferentPackageChild defaultMethod");
    }
}
  1. 父类中的private方法不能被重写:因为private方法只能在声明它的类内部被访问,子类根本无法访问到父类的private方法,所以也就不存在重写的概念。不过,子类可以定义一个与父类private方法签名相同的方法,但这并不是重写,而是一个全新的方法。
class PrivateParent {
    private void privateMethod() {
        System.out.println("PrivateParent privateMethod");
    }
}

class PrivateChild extends PrivateParent {
    // 这不是重写,而是一个新方法
    public void privateMethod() {
        System.out.println("PrivateChild privateMethod");
    }
}

方法重写访问权限规则的实际应用场景

  1. 框架开发:在大型框架中,经常会使用到方法重写。框架提供一些基础类,其中的某些方法会被设计成允许子类重写,以实现定制化的功能。例如,在Java的Swing框架中,JFrame类有一些方法可以被子类重写来处理窗口的各种事件。为了确保框架的稳定性和安全性,这些可重写方法的访问权限必须遵循一定的规则。如果框架中的方法是public,那么子类重写时也必须是public,这样其他使用框架的开发者才能正确地通过父类引用调用到子类重写的方法。
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class MyFrame extends JFrame {
    public MyFrame() {
        JButton button = new JButton("Click me");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Button clicked");
            }
        });
        add(button);
        setSize(300, 200);
        setVisible(true);
    }
}

public class SwingExample {
    public static void main(String[] args) {
        new MyFrame();
    }
}

在这个例子中,ActionListener接口中的actionPerformed方法是public的,当我们在匿名内部类中实现这个方法时,也必须是public,这遵循了方法重写的访问权限规则。

  1. 代码复用与扩展:在开发中,我们常常通过继承来复用现有类的代码,并通过方法重写来扩展功能。例如,在一个游戏开发项目中,有一个Character类,其中有一个move方法。不同类型的角色(如战士、法师等)继承自Character类,并根据自身特点重写move方法。如果Character类中的move方法是protected,那么子类重写时可以保持protected权限,以确保只有子类和同一包内的类可以访问这个方法,同时也能实现多态。
class Character {
    protected void move() {
        System.out.println("Character is moving");
    }
}

class Warrior extends Character {
    @Override
    protected void move() {
        System.out.println("Warrior is running");
    }
}

class Mage extends Character {
    @Override
    protected void move() {
        System.out.println("Mage is teleporting");
    }
}

注意事项

  1. 返回类型的兼容性:在Java 5及以上版本中,重写方法的返回类型可以是被重写方法返回类型的子类型,这被称为协变返回类型。例如,如果父类方法返回Animal类型,子类重写方法可以返回Dog类型,因为DogAnimal的子类。但同时,重写方法的访问权限规则依然适用。
class Animal {
    public Animal getInstance() {
        return new Animal();
    }
}

class Dog extends Animal {
    @Override
    public Dog getInstance() {
        return new Dog();
    }
}
  1. 异常处理:重写方法抛出的异常不能比被重写方法抛出的异常更宽泛。例如,如果父类方法抛出IOException,子类重写方法不能抛出Exception,因为ExceptionIOException的父类,这样会破坏调用者对异常的预期处理。不过,子类重写方法可以不抛出异常,或者抛出更具体的异常。
class ExceptionParent {
    public void method() throws IOException {
        // 可能抛出IOException
    }
}

class ExceptionChild extends ExceptionParent {
    @Override
    public void method() throws FileNotFoundException {
        // 可以抛出更具体的异常
    }
}

class AnotherExceptionChild extends ExceptionParent {
    @Override
    public void method() {
        // 也可以不抛出异常
    }
}
  1. 静态方法与实例方法的区别:静态方法不能被重写,因为静态方法属于类本身,而不是对象。如果子类定义了一个与父类静态方法签名相同的静态方法,这被称为静态方法隐藏,而不是重写。并且,静态方法的访问权限规则与实例方法类似,即子类隐藏的静态方法访问权限不能比父类更严格。
class StaticParent {
    public static void staticMethod() {
        System.out.println("StaticParent staticMethod");
    }
}

class StaticChild extends StaticParent {
    // 这是静态方法隐藏,不是重写
    public static void staticMethod() {
        System.out.println("StaticChild staticMethod");
    }
}

总结与深入理解

Java多态下方法重写的访问权限规则是确保代码安全性、一致性和可扩展性的重要机制。通过遵循这些规则,我们可以在实现多态的同时,保证不同类之间的访问控制合理。在实际开发中,无论是开发小型应用还是大型框架,都需要准确理解和应用这些规则。

在设计类层次结构和方法重写时,要从整体的访问控制策略出发。如果希望某个方法能够被子类自由扩展并在不同场景下使用,通常会将其定义为publicprotected。而如果该方法只在类内部或同一包内使用,那么可以使用默认权限或private

同时,结合返回类型的协变、异常处理以及静态方法与实例方法的不同规则,我们能够编写出更加健壮和灵活的Java代码。只有深入理解并熟练运用这些规则,才能在Java编程中充分发挥多态的优势,打造出高质量的软件系统。

希望通过本文对Java多态下方法重写访问权限规则的详细介绍,能帮助读者在日常开发中避免因访问权限问题导致的错误,更好地利用Java的多态特性进行编程。