各位道友们好!今天咱们来聊聊Java中两个容易混淆的概念:方法重载(Overloading)和方法重写(Overriding)。这俩兄弟长得像,但性格完全不同,就像吕洞宾的剑法——看似相似,实则各有千秋!
方法重载就像是一个门派里的师兄弟,虽然名字相同(方法名相同),但各有各的独门绝技(参数列表不同)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class KungFuMaster { // 基础招式 public void attack() { System.out.println("普通拳法!"); } // 重载版本1:带武器 public void attack(String weapon) { System.out.println("使用" + weapon + "攻击!"); } // 重载版本2:带武器和力量 public void attack(String weapon, int power) { System.out.println("使用" + weapon + "发动" + power + "成功力!"); } // 重载版本3:不同参数顺序 public void attack(int power, String weapon) { System.out.println(power + "成功力施展" + weapon + "!"); } } |
必要条件:
调用示例:
|
1 2 3 4 5 |
KungFuMaster master = new KungFuMaster(); master.attack(); // 普通拳法! master.attack("宝剑"); // 使用宝剑攻击! master.attack("宝剑", 8); // 使用宝剑发动8成功力! master.attack(8, "宝剑"); // 8成功力施展宝剑! |
反例1:仅靠返回类型不同不能重载
|
1 2 3 4 5 |
class ErrorExample { public void show() { } public int show() { return 1; } // 编译错误! } // 原因:编译器无法区分调用哪个方法,只看方法名和参数列表 |
反例2:参数列表必须真正不同
|
1 2 3 4 5 |
class ErrorExample2 { public void train(String student) { } public void train(String master) { } // 编译错误! } // 原因:参数名不同不算重载,要看参数类型 |
方法重写就像是儿子继承父亲的手艺,但青出于蓝而胜于蓝——方法签名相同,但实现更优秀。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 父类:老拳师 class OldMaster { public void secretMove() { System.out.println("老式绝招:降龙十八掌"); } public final void forbiddenMove() { System.out.println("禁招:不得传授"); } } // 子类:新拳师 class NewMaster extends OldMaster { @Override public void secretMove() { System.out.println("新式绝招:升级版降龙十八掌"); } // 不能重写final方法 // @Override // public void forbiddenMove() { } // 编译错误! } |
必要条件:
@Override注解的重要性:
|
1 2 3 4 5 6 7 8 9 10 |
class SmartMaster extends OldMaster { @Override // 加上这个注解更安全 public void secretMove() { System.out.println("智能绝招"); } // 如果没有@Override,拼写错误不会被发现 public void secretmove() { // 本想重写,但方法名写错了 System.out.println("这其实是个新方法,不是重写!"); } } |
| 特性 | 方法重载 (Overloading) | 方法重写 (Overriding) |
|---|---|---|
| 发生位置 | 同一个类中 | 父子类之间 |
| 方法签名 | 必须不同 | 必须相同 |
| 返回类型 | 可以不同 | 必须相同或协变 |
| 访问权限 | 可以不同 | 不能更严格 |
| 绑定时机 | 编译时绑定 | 运行时绑定 |
| 多态性 | 不支持多态 | 支持多态 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Animal { // 重载示例 public void eat() { System.out.println("动物吃东西"); } public void eat(String food) { System.out.println("动物吃" + food); } // 可重写的方法 public void sleep() { System.out.println("动物睡觉"); } } class Dog extends Animal { // 重写父类方法 @Override public void sleep() { System.out.println("狗狗趴着睡觉"); } // 重载:新增方法 public void eat(String food, int amount) { System.out.println("狗狗吃" + amount + "份" + food); } } |
协变返回类型允许重写方法时返回父类方法返回类型的子类型:
|
1 2 3 4 5 6 7 8 9 10 11 |
class Fruit { public Fruit getFruit() { return new Fruit(); } } class Apple extends Fruit { @Override public Apple getFruit() { // 返回Apple而不是Fruit return new Apple(); } } |
反例:Java 5之前的问题
|
1 2 3 4 5 6 7 8 9 |
// Java 5之前的写法(需要强制转型) class OldApple extends Fruit { @Override public Fruit getFruit() { return new Apple(); // 返回Fruit类型 } } Apple apple = (Apple) new OldApple().getFruit(); // 需要强制转型 // 原因:早期Java要求返回类型必须完全一致 |
构造器重载是方法重载的特殊形式,用于以不同方式创建对象:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class MartialArtsSchool { private String name; private int studentCount; // 默认构造器 public MartialArtsSchool() { this("无名武馆", 10); } // 重载构造器1 public MartialArtsSchool(String name) { this(name, 20); } // 重载构造器2 public MartialArtsSchool(String name, int studentCount) { this.name = name; this.studentCount = studentCount; } } |
反例:构造器重载的常见错误
|
1 2 3 4 5 6 7 8 9 10 11 |
class ErrorSchool { private String name; public ErrorSchool() { // 忘记初始化name } public ErrorSchool(String name) { this(); // 调用无参构造器 this.name = name; // 但无参构造器没初始化 } } // 原因:构造器链调用时要确保所有路径都正确初始化 |
重写时经常需要调用父类的原始实现:
|
1 2 3 4 5 6 7 |
class ImprovedMaster extends OldMaster { @Override public void secretMove() { super.secretMove(); // 先调用父类方法 System.out.println("追加新招式:九阳神功"); } } |
反例:错误使用super
|
1 2 3 4 5 6 7 8 |
class WrongMaster extends OldMaster { @Override public void secretMove() { System.out.println("我的新招式"); super.secretMove(); // 顺序可能不对 } } // 可能的问题:业务逻辑要求先执行父类方法 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// 基础武术类 class MartialArt { protected String name; public MartialArt(String name) { this.name = name; } // 可重写的方法 public void practice() { System.out.println("练习" + name); } public void practice(int hours) { System.out.println("练习" + name + hours + "小时"); } } // 具体武术类型 class TaiChi extends MartialArt { public TaiChi() { super("太极拳"); } @Override public void practice() { System.out.println("以柔克刚:" + name); } // 重载:新增练习方式 public void practice(String style) { System.out.println("练习" + style + "式" + name); } } class ShaolinKungfu extends MartialArt { public ShaolinKungfu() { super("少林功夫"); } @Override public void practice() { super.practice(); // 调用父类方法 System.out.println("追加:金钟罩铁布衫"); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class MartialArtsTest { public static void main(String[] args) { MartialArt[] arts = { new TaiChi(), new ShaolinKungfu() }; for (MartialArt art : arts) { art.practice(); // 多态:调用实际类型的重写方法 } TaiChi taiChi = new TaiChi(); taiChi.practice(2); // 重载:练习太极拳2小时 taiChi.practice("陈式"); // 重载:练习陈式太极拳 } } |
|
1 2 3 4 5 6 7 8 9 10 11 |
class ConfusionExample { public void method(Object obj) { System.out.println("Object版本"); } public void method(String str) { System.out.println("String版本"); } } ConfusionExample example = new ConfusionExample(); example.method(null); // 输出:String版本 // 原因:重载解析选择最具体的类型 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Parent { public static void staticMethod() { System.out.println("父类静态方法"); } } class Child extends Parent { public static void staticMethod() { System.out.println("子类静态方法"); } } Parent obj = new Child(); obj.staticMethod(); // 输出:父类静态方法(不是多态!) // 原因:静态方法不支持重写,只支持隐藏 |
重载重写要分清,同名方法各不同
重载同 class 参数异,编译时分显神通
重写父子 signature 同,运行时分多态功
@Override 是护身符,拼写错误无处藏
静态 final 不能改,private 方法不继承
掌握这两招绝技,Java 江湖任你闯!
记住这些诀窍,你就能在Java的江湖中游刃有余地使用重载和重写了!下次再见,道友们!