原文:geeks4geeks
子类可以重新定义父类的方法,这种属性被称为方法重写(Overriding)。签名(返回类型,参数类型,参数数量)保持和父类中定义的一致,完成方法重写以实现运行时多态(重写的方法是根据调用它的对象被调用,而不是根据它的引用类型)。
// 简单的演示重写的例子
// 父类
class Parent{
void show() {
System.out.println("Parent's show()");
}
}
// 子类
class Child extends Parent{
// 重写父类的方法
void show() {
System.out.println("Child's show()");
}
}
// 测试类
class Test {
public static void main(String[] args) {
// 如果父类引用父类的对象,则父类的 show 方法被调用
Parent obj1 = new Parent();
obj1.show();
// 如果父类引用子类的对象,则子类的 show 方法被调用
// 这种被称为运行时多态
Parent obj2 = new Child();
obj2.show();
}
}
输出:
Parent's show()
Child's show()
一些有趣的事实:
- C++ 中需要 virtual 关键字以实现重写或运行时多态。Java 中方法默认是 virtual 的。
- 可以有多级的重写:
// 简单的演示多级重写的例子
// 父类
class Parent {
void show() {
System.out.println("Parent's show()");
}
}
// 子类
class Child extends Parent {
// 重写父类的方法
void show() {
System.out.println("Child's show()");
}
}
// 孙类
class GrandChild extends Child {
// 重写子类的方法
void show() {
System.out.println("GrandChild's show()");
}
}
// 测试类
class Test {
public static void main(String[] args) {
Parent obj1 = new GrandChild();
obj1.show();
}
}
输出:
GrandChild's show()
- 如果不想让一个方法被重载,可以用 final 关键字修饰它
// 简单的演示 final 关键字重写的例子
class Parent {
// 不能被重写
final void show() { }
}
class Child extends Parent {
// 会产生错误
void show() { }
}
输出:
error: show() in Child cannot override show() in Parent
void show() { }
^
overridden method is final
- 重写的方法的访问限制不能收窄,只能扩大。如父类中是 protected 方法 ,子类不能以 private 限制符重写方法。
- Private 的方法不能被重写,因为在 private 方法在编译时绑定会它的引用类型。
- 我们可以给子类中与父类相同签名的方法加上 static 关键词,但这不认为是重写,因为没有任何运行时多态。
- 我们可以在重写的方法中使用 super 关键字调用父类的方法。
// 简单的例子演示 super 关键字重写的例子
// 父类
class Parent {
void show() {
System.out.println("Parent's show()");
}
}
// 子类
class Child extends Parent {
// 重写父类的 show 方法
void show() {
super.show();
System.out.println("Child's show()");
}
}
// 测试类
class Test {
public static void main(String[] args) {
Parent obj = new Child();
obj.show();
}
}
输出:
Parent's show()
Child's show()
应用
重写的方法允许我们调用任何派生类的方法,甚至不必知道派生类对象的种类。
上图的管理软件中基类 Employee 有不同的方法,如 raiseSalary(), transfer(), promote(), salary()等。不同的子类 Manager 和 Clerk 可能对这些父类的方法有自己的实现。在实际使用场景中,我们只需要传递一份完整的员工名单,而不必知道他所属的类型是 Manager 或 Clerk 之类的。例如,我们只需通过遍历名单给每位员工 raiseSalary()
,每位员工都有自己的 raiseSalary()
逻辑,我们不必知道每个员工对应的类型。每个员工只要有 raiseSalary()
方法,便会被调用。
// 简单的演示重写应用的例子
// 基类
class Employee {
public static int base = 10000;
int salary() {
return base;
}
}
// 派生类
class Manager extends Employee {
// This method overrides show() of Parent
int salary() {
return base + 20000;
}
}
// 派生类
class Clerk extends Employee {
// This method overrides show() of Parent
int salary() {
return base + 10000;
}
}
// 测试类
class Test {
// 打印雇员的薪资
static void printSalary(Employee e) {
System.out.println(e.salary());
}
public static void main(String[] args) {
Employee obj1 = new Manager();
System.out.print("Manager's salary : ");
printSalary(obj1);
Employee obj2 = new Clerk();
System.out.print("Clerk's salary : ");
printSalary(obj2);
}
}
输出:
Manager's salary : 30000
Clerk's salary : 20000