一、基本概念對比
特性 | 方法重寫(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面向對象編程的重要基礎。