RHODES ISLAND // PERSONAL ARCHIVE

关之水瑶 · 作战记录

记录技术、生活与正在进行的思考。欢迎来到我的个人情报终端。

SYSTEM ONLINE / 2026

3. 方法重写(Override)和方法重载(Overload)的区别?(面试官考核目标:考察编译期静态绑定与运行期动态绑定的区别)

方法重写(Override)与方法重载(Overload)的区别

一句话回答

  • 重载(Overload):同一个类中,方法名相同、参数列表不同,是编译期静态绑定。
  • 重写(Override):子类重新实现父类的方法,方法签名相同,是运行期动态绑定。
  • 本质区别:重载看“编译时选哪个方法”,重写看“运行时调用哪个对象的方法”。

1. 核心对比

维度 重载 Overload 重写 Override
发生位置 同一个类中(或父子类中也可构成重载) 子类与父类之间
方法名 相同 相同
参数列表 必须不同(类型、个数、顺序) 必须相同
返回值 可以不同 必须相同或是父类返回值的子类型(协变返回)
访问修饰符 无关 不能比父类更严格
异常 无关 不能抛出比父类更宽的受检异常
绑定时机 编译期(静态绑定) 运行期(动态绑定)
多态类型 编译时多态 运行时多态
关键修饰 无 @Override(建议加)
决定因素 参数的静态类型 对象的实际类型

记忆:重载看参数,重写看对象。重载编译定,重写运行定。


2. 重载 Overload

定义

同一个类中,方法名相同,参数列表不同。
参数列表不同包括:参数个数不同、参数类型不同、参数顺序不同。

例子

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public String add(String a, String b) {
        return a + b;
    }
}

不能构成重载的情况

public int add(int a, int b) { return a + b; }

// 编译错误:仅返回值不同,不构成重载
public double add(int a, int b) { return a + b; }

// 编译错误:仅参数名不同,不构成重载
public int add(int x, int y) { return x + y; }

只有返回值不同、参数名不同、访问修饰符不同、抛异常不同,都不构成重载。

编译期如何选择

编译器根据方法名 + 参数静态类型在编译期确定调用哪个方法。

Calculator c = new Calculator();
c.add(1, 2);       // 编译期确定调用 add(int, int)
c.add(1.0, 2.0);   // 编译期确定调用 add(double, double)

重载解析顺序(简化)

  1. 精确匹配
  2. 基本类型自动提升(如 int → long → float → double)
  3. 自动装箱/拆箱
  4. 可变参数
void f(int x)    { System.out.println("int"); }
void f(long x)   { System.out.println("long"); }
void f(Integer x){ System.out.println("Integer"); }

f(1); // 输出 int,精确匹配优先

注意:null 作为参数时可能产生歧义,编译报错。


3. 重写 Override

定义

子类定义与父类方法签名相同的方法,覆盖父类实现。

例子

class Animal {
    public void makeSound() {
        System.out.println("动物叫声");
    }
}

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

重写规则

  1. 方法名相同
  2. 参数列表相同
  3. 返回值相同,或为父类返回值的子类型(协变返回)
  4. 访问权限不能更严格:父类 protected,子类不能是 private
  5. 不能抛出更宽的受检异常
  6. 不能重写 final、static、private 方法(static 是隐藏,private 不可见)
  7. 建议加 @Override,让编译器帮你检查

协变返回示例

class Parent {
    Number get() { return 1; }
}

class Child extends Parent {
    @Override
    Integer get() { return 1; } // 返回值是 Number 的子类型,合法
}

运行期如何选择

运行时根据对象的实际类型决定调用哪个方法,这叫动态绑定 / 虚方法分派。

Animal a = new Dog();
a.makeSound(); // 输出“汪汪”,看实际对象是 Dog,不是看变量类型 Animal

变量 a 的静态类型是 Animal,编译期只检查 Animal 有没有 makeSound();
运行期实际对象是 Dog,所以调用 Dog.makeSound()。


4. 静态绑定 vs 动态绑定

静态绑定(编译期)

  • 发生在编译期。
  • 依据:变量的静态类型、方法签名、参数类型。
  • 适用:重载、static 方法、private 方法、final 方法、构造器。
  • 优点:快,编译期就能确定。
class A {
    static void hello() { System.out.println("A"); }
}

class B extends A {
    static void hello() { System.out.println("B"); }
}

A a = new B();
a.hello(); // 输出 A,静态方法看静态类型,编译期绑定

动态绑定(运行期)

  • 发生在运行期。
  • 依据:对象的实际类型。
  • 适用:重写(实例方法)。
  • 实现:JVM 通过虚方法表(vtable)或方法表进行动态分派。
class A {
    void hello() { System.out.println("A"); }
}

class B extends A {
    @Override
    void hello() { System.out.println("B"); }
}

A a = new B();
a.hello(); // 输出 B,实例方法看实际类型,运行期绑定

对比记忆:
static 方法看左边(静态类型),实例重写方法看右边(实际对象)。


5. 重载与重写结合的例子

class Parent {
    public void print(int x) {
        System.out.println("Parent int");
    }

    public void print(String s) {
        System.out.println("Parent String");
    }
}

class Child extends Parent {
    @Override
    public void print(int x) {
        System.out.println("Child int");
    }

    // 这是重载,不是重写:参数列表不同
    public void print(double x) {
        System.out.println("Child double");
    }
}

Parent p = new Child();
p.print(1);       // 运行期重写:Child int
p.print("hi");    // Parent String

Child c = new Child();
c.print(1);       // Child int
c.print(1.0);     // 编译期重载:Child double

关键:
先由编译器按静态类型做重载解析,选出候选方法;
再在运行期按实际类型做动态分派,决定最终执行哪个重写版本。


6. 面试口述版

重载和重写是两回事。

重载是同一个类中方法名相同、参数列表不同,和返回值、访问修饰符无关。它在编译期根据参数的静态类型确定调用哪个方法,属于静态绑定,也叫编译时多态。

重写是子类重新实现父类的方法,要求方法名、参数列表相同,返回值相同或是子类型,访问权限不能更严格,不能抛出更宽的受检异常。它在运行期根据对象的实际类型决定调用哪个方法,属于动态绑定,也叫运行时多态。JVM 通过虚方法表实现动态分派。

一句话:重载看参数,编译期定;重写看对象,运行期定。


7. 常见追问

  • 重载能发生在父子类之间吗?
    能。子类定义与父类同名但参数不同的方法,也构成重载;但通常不推荐,容易混淆。

  • static 方法能被重写吗?
    不能。子类定义同名同参的 static 方法是隐藏(hide),不是重写,按静态类型绑定。

  • private 方法能被重写吗?
    不能。private 方法对子类不可见,子类定义同名方法只是新方法。

  • final 方法能被重写吗?
    不能。

  • 构造器能被重写吗?
    不能。构造器不能被继承,也谈不上重写,但可以重载。

  • 重写时返回值能不同吗?
    可以协变:子类返回值是父类返回值的子类型。

  • 为什么重写不能缩小访问权限?
    违反里氏替换原则:父类引用能调用的方法,子类对象也必须能调用。

  • 动态绑定怎么实现的?
    JVM 为每个类维护方法表,对象头指向类元数据,调用实例方法时按实际类型查表分派。

posted @ 2026-09-24 17:13  关之水瑶  阅读(19)  评论(0)    收藏  举报