重写与重载

核心概念

重写(Override)和重载(Overload)是Java中多态性的两种重要表现形式:

  • 重写(Override):运行时多态,发生在继承关系中
  • 重载(Overload):编译时多态,发生在同一个类中

理解它们的机制和区别,是掌握Java面向对象编程的关键。


一、方法重写(Override)

1.1 基本语法

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

class Child extends Parent {
    // 重写父类方法
    @Override
    public void method(int x) {
        System.out.println("Child method");
    }
}

// 使用
Parent p = new Child();
p.method(10);  // 输出:Child method(运行时多态)

1.2 重写的规则(两同两小一大)

规则 说明 示例
方法名相同 必须完全一致 method()
参数列表相同 参数类型和顺序 method(int x, String y)
返回类型相同或更小(协变返回) 可以是原返回类型的子类 Object → String
异常相同或更小 不能抛出更大的异常 IOException → FileNotFoundException
访问权限相同或更大 不能降低可见性 protected → public

1.3 @Override注解

class Parent {
    public void method() {}
}

class Child extends Parent {
    // ✅ 正确:使用@Override检查重写
    @Override
    public void method() {}

    // ❌ 错误:方法名写错,编译器会报错
    @Override
    public void methd() {}  // 编译错误
}

1.4 协变返回类型

class Animal {
    public Animal getInstance() {
        return new Animal();
    }
}

class Dog extends Animal {
    // 协变返回:返回类型可以是原返回类型的子类
    @Override
    public Dog getInstance() {
        return new Dog();
    }
}

1.5 异常处理

class Parent {
    public void method() throws IOException {
        // 抛出较大的异常
    }
}

class Child extends Parent {
    // ✅ 正确:抛出更小的异常
    @Override
    public void method() throws FileNotFoundException {
        // 抛出更具体的异常
    }

    // ❌ 错误:不能抛出更大的异常
    // @Override
    // public void method() throws Exception { }  // 编译错误
}

二、方法重载(Overload)

2.1 基本语法

class Calculator {
    // 重载:方法名相同,参数不同
    public int add(int a, int b) {
        return a + b;
    }

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

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

// 使用
Calculator calc = new Calculator();
calc.add(1, 2);         // 调用 int add(int, int)
calc.add(1.0, 2.0);     // 调用 double add(double, double)
calc.add(1, 2, 3);      // 调用 int add(int, int, int)

2.2 重载的规则

规则 说明 示例
方法名相同 必须完全一致 add()
参数列表不同 参数类型、数量、顺序 add(int,int) vs add(double,double)
返回类型无关 仅靠返回类型区分无效 int add() vs double add() 是错误的

2.3 重载的类型匹配

class OverloadDemo {
    public void method(int x) {
        System.out.println("int");
    }

    public void method(double x) {
        System.out.println("double");
    }

    public void method(Integer x) {
        System.out.println("Integer");
    }
}

// 调用顺序
OverloadDemo demo = new OverloadDemo();
demo.method(10);        // 输出:int(精确匹配)
demo.method(10.0);      // 输出:double
demo.method(Integer.valueOf(10));  // 输出:Integer

2.4 可变参数重载

class VarargsDemo {
    public void method(String... args) {
        System.out.println("String...");
    }

    public void method(int... args) {
        System.out.println("int...");
    }
}

VarargsDemo demo = new VarargsDemo();
demo.method("a", "b");      // 输出:String...
demo.method(1, 2, 3);       // 输出:int...

三、重写 vs 重载

3.1 核心区别

特性 重写(Override) 重载(Overload)
发生位置 继承关系中的子类 同一个类中
方法名 相同 相同
参数列表 相同 不同
返回类型 相同或协变 无关
多态类型 运行时多态 编译时多态
访问权限 不能降低 无限制

3.2 多态性对比

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

class Child extends Parent {
    // 重写:运行时多态
    @Override
    public void method(int x) {
        System.out.println("Child");
    }

    // 重载:编译时多态
    public void method(String s) {
        System.out.println("Child-String");
    }
}

Parent p = new Child();
p.method(10);       // 输出:Child(重写,运行时)
p.method("hello");  // 编译错误(重载需要具体类型)

3.3 方法调用的绑定

class Parent {
    public static void staticMethod() {
        System.out.println("Parent static");
    }

    public void instanceMethod() {
        System.out.println("Parent instance");
    }
}

class Child extends Parent {
    public static void staticMethod() {
        System.out.println("Child static");
    }

    @Override
    public void instanceMethod() {
        System.out.println("Child instance");
    }
}

Parent p = new Child();
p.staticMethod();       // 输出:Parent static(静态绑定)
p.instanceMethod();     // 输出:Child instance(动态绑定)

绑定类型:

  • 静态绑定(编译时):static、private、final方法
  • 动态绑定(运行时):普通实例方法

四、高频面试题

Q1: 重写和重载有什么区别?

答:

  • 重写:继承关系中子类覆盖父类方法,运行时多态
  • 重载:同一个类中方法名相同但参数不同,编译时多态
  • 重写关注继承,重载关注同一类中的多个方法

Q2: 为什么static方法不能被重写?

答:

  • static方法属于类,不属于对象实例
  • 静态方法在编译时绑定,不支持运行时多态
  • 子类中定义同名static方法是隐藏,不是重写

Q3: 协变返回类型是什么?

答:

  • 子类重写方法可以返回父类方法返回类型的子类
  • 增加了方法返回值的灵活性
  • JDK 5引入的特性

Q4: 重载方法是如何匹配的?

答:

  • 精确匹配优先
  • 基本类型自动提升(int → long → float → double)
  • 装箱/拆箱
  • 可变参数最后匹配

Q5: @Override注解的作用?

答:

  • 编译器检查是否真的重写了父类方法
  • 防止方法名拼写错误
  • 提高代码可读性

五、最佳实践

✅ 推荐做法

// 1. 使用@Override注解
@Override
public void method() { }

// 2. 重载方法保持语义一致
public void process(int value) { }
public void process(String value) { }

// 3. 谨慎使用可变参数重载
public void method(String... args) { }
public void method(int... args) { }

// 4. 协变返回类型
@Override
public SubClass getInstance() {
    return new SubClass();
}

❌ 避免做法

// 1. 不要重载具有歧义的方法
public void method(int x) { }
public void method(Integer x) { }  // 容易混淆

// 2. 避免过度依赖重载
public void process(Object obj) { }
public void process(String str) { }  // 可能导致意外调用

// 3. 不要滥用static方法重写
public static void method() { }  // 不是真正的重写

相关链接


记忆口诀

重写子类覆盖,运行时多态显
参数类型不变,返回可协变
重载同名方法,参数来区分
静态方法不变,编译时绑定深
@Override护航法,检查重写无烦恼

相关笔记

本篇讲清了"怎么重写/怎么重载";方法重载和方法重写编译时多态和运行时多态 则从"多态"角度再切一刀——重载绑定发生在编译期、重写绑定发生在运行期,这正是运行时多态的实现机制。三篇对照读,"两个绑定时机"这个面试高频点就闭环了。


⬅️ 访问权限 🏠 00-Java ➡️ 面向对象or面向过程