一、基本概念对比
特性 | 方法重写(Override) | 方法重载(Overload) |
---|---|---|
定义 | 子类重新定义父类中已有的方法 | 同一个类中多个同名方法,参数不同 |
作用范围 | 继承关系中(父子类之间) | 同一个类内 |
方法签名 | 必须相同(方法名+参数列表) | 必须不同(至少参数类型、个数或顺序不同) |
返回类型 | 相同或是父类方法返回类型的子类型(协变返回) | 可以不同 |
访问权限 | 不能比父类方法更严格 | 可以不同 |
异常抛出 | 不能抛出比父类方法更多/更宽泛的检查型异常 | 可以不同 |
静态性 | 不能重写静态方法(隐藏不算重写) | 可以重载静态方法 |
二、方法重写(Override)
1. 重写规则
- 必须继承关系(子类重写父类方法)
- 方法名、参数列表必须完全相同
- 返回类型相同或是其子类(Java 5+支持协变返回)
- 访问修饰符不能比父类更严格(public > protected > default > private)
- 不能抛出比父类方法更多的检查异常(非检查异常不受限)
2. 代码示例
class Animal {protected void makeSound() {System.out.println("Animal makes sound");}public Animal getInstance() {return new Animal();}
}class Dog extends Animal {// 正确重写 - 访问权限更宽松@Overridepublic void makeSound() {System.out.println("Dog barks");}// 协变返回类型 - 返回Dog而非Animal@Overridepublic Dog getInstance() {return new Dog();}
}
3. @Override注解
- 非强制但强烈建议使用
- 帮助编译器检查是否满足重写条件
- 提高代码可读性
class Cat extends Animal {@Override // 如果拼写错误会报编译错误public void makeSound() {System.out.println("Cat meows");}
}
4. 不能重写的情况
- private方法:对子类不可见
- final方法:禁止重写
- static方法:属于类而非实例(可以"隐藏"但不是重写)
三、方法重载(Overload)
1. 重载规则
- 必须在同一个类中
- 方法名必须相同
- 参数列表必须不同(类型、个数或顺序)
- 返回类型可以不同(仅返回类型不同不算重载)
- 访问修饰符可以不同
- 可以抛出不同的异常
2. 代码示例
class Calculator {// 整数加法public int add(int a, int b) {return a + b;}// 重载1 - 参数类型不同public double add(double a, double b) {return a + b;}// 重载2 - 参数个数不同public int add(int a, int b, int c) {return a + b + c;}// 重载3 - 参数顺序不同public String add(String s, int n) {return s + n;}public String add(int n, String s) {return n + s;}
}
3. 自动类型转换与重载
class Printer {void print(int i) {System.out.println("int: " + i);}void print(double d) {System.out.println("double: " + d);}void print(String s) {System.out.println("String: " + s);}
}public class Main {public static void main(String[] args) {Printer p = new Printer();p.print(10); // 调用print(int)p.print(10.0); // 调用print(double)p.print("10"); // 调用print(String)p.print(10L); // 自动转换,调用print(double)}
}
四、特殊场景分析
1. 父子类中的重载
class Parent {void process(int i) {System.out.println("Parent int: " + i);}
}class Child extends Parent {// 这是重载而非重写void process(double d) {System.out.println("Child double: " + d);}
}public class Test {public static void main(String[] args) {Child c = new Child();c.process(5); // 调用Parent.process(int)c.process(5.0); // 调用Child.process(double)}
}
2. 可变参数与重载
class VarArgsDemo {void process(int... nums) {System.out.println("Processing numbers");}void process(String... strs) {System.out.println("Processing strings");}// 会与上面两个方法冲突// void process(int n, String... strs) { ... }
}
3. 泛型方法重载
class GenericOverload {// 编译错误 - 类型擦除后签名相同// void process(List<String> list) { ... }// void process(List<Integer> list) { ... }// 合法重载void process(String s) { ... }void process(Integer i) { ... }
}
五、面试常见问题
1. 重写与重载的区别是什么?
- 重写:父子类间,相同签名,实现不同
- 重载:同类中,不同签名,功能相似
2. 构造方法能重写吗?
- 不能,构造方法不能被继承
- 但可以重载(一个类中多个构造方法)
3. main方法能重写或重载吗?
- 不能重写(static方法)
- 可以重载(但只有public static void main(String[])是入口)
4. 以下代码输出什么?
class A {void m1(Object o) { System.out.println("A"); }
}class B extends A {void m1(String s) { System.out.println("B"); }
}public class Test {public static void main(String[] args) {A a = new B();a.m1("hello"); // 输出"A"(因为m1(String)没有重写m1(Object))}
}
六、最佳实践
-
重写时总是使用@Override:
- 防止意外创建重载而非重写
- 提高代码可读性
-
重载方法应保持功能一致:
- 不同参数形式实现相同功能
- 避免让使用者困惑
-
谨慎使用可变参数重载:
- 容易导致调用歧义
- 编译器可能无法确定最佳匹配
-
避免过于复杂的重载设计:
- 参数类型差异应明显
- 考虑使用不同方法名提高可读性
-
注意自动装箱与重载:
void process(Integer i) { ... }
void process(long l) { ... }process(5); // 调用process(long),因为不需要自动装箱
通过深入理解方法重写和重载的区别与应用场景,可以设计出更加清晰、灵活的类结构,这是Java面向对象编程的重要基础。