JAVA面试|什么是重写?规则是什么?

一、什么是重写(Override)?(它解决了什么问题?)

想象一个家庭:

父亲(父类):有一个技能叫做饭(),他能做一道普通的“西红柿炒鸡蛋”。

儿子(子类):继承了父亲,也会做饭()。但是儿子去新东方进修过,他做的“西红柿炒鸡蛋”会颠勺、摆盘更精美、还加了独特的调料。

现在,当家里来客人说“让你家会做饭的人来做道菜”时:

如果让父亲出手,端上来的就是普通的西红柿炒鸡蛋。

如果让儿子出手,端上来的就是豪华升级版的西红柿炒鸡蛋。

儿子对“做饭”这个技能的全新实现,就叫做“重写”(Override)。

官方定义

在面向对象编程中,当子类觉得从父类继承来的某个方法(技能)的实现细节不满足自己的需求时,它可以在自己内部重新编写这个方法的实现逻辑。这个过程就叫重写。

核心目的:

实现多态:同一个方法调用(如做饭()),作用于不同的对象(父亲或儿子),会产生不同的行为(普通菜或豪华菜)。这是面向对象编程最强大的特性之一。

扩展和定制:子类可以在保留父类方法名称和基本约定的前提下,提供自己独特的实现,从而扩展父类的功能。

二、重写的规则(想成功重写,必须遵守的“君子协定”)

重写不是乱写,必须遵守一系列规则,Java编译器会严格检查这些规则。规则的核心思想是:子类的新方法不能“欺骗”调用者。调用者以为自己在调用一个父类的方法,那么子类的方法就不能在基本约定上“耍花样”。

以下是必须遵守的规则:

1. 方法签名必须相同

方法名必须完全相同。

参数列表(参数的个数、类型、顺序)必须完全相同。

返回值类型:

在Java 5及以后,返回值类型可以相同,也可以是父类方法返回值类型的子类(这被称为“协变返回类型”)。但不能是毫不相关的类型。

通俗理解:父亲说“我做饭会返回一盘菜”。儿子可以说“我做饭会返回一盘五星级菜”(五星级菜是菜的子类),但不能说“我做饭会返回一个音乐CD”。

2. 访问权限不能更严格(但可以更宽松)

子类重写方法的访问权限(public, protected, 默认, private)不能比父类被重写方法的权限更严格。

通俗理解:父亲承诺他的方法对所有人都是public(公开的)。儿子重写后不能把它变成private(私有的),这样会违背父亲的承诺。但儿子可以保持为public,或者甚至更宽松(虽然通常无法更宽松,因为public已经是最宽松的了)。常见的规则是:

父类方法是 public,子类必须是 public。

父类方法是 protected,子类可以是 protected 或 public。

3. 异常抛出不能更宽泛

子类重写方法所声明抛出的异常(通常是checked exception)不能比父类方法声明的异常更宽泛(即不能是父类异常父类)。

通俗理解:父亲说“我做饭可能会抛出一个做饭烧糊了异常”。儿子可以说“我做饭可能会抛出一个翻炒太用力异常”(它是做饭烧糊了异常的子类),或者干脆不抛出异常。但不能说“我做饭可能会抛出一个所有厨房灾难异常”(它是做饭烧糊了异常的父类),这样调用者就无法做精确的异常处理了。


三、代码示例和规则验证

父类(Father.java)

public class Father {

// 父类有一个public方法,返回一个Car对象,声明抛出CookingException异常

public Car cook() throws CookingException {

System.out.println("父亲做了一盘普通的西红柿炒鸡蛋。");

return new Car(); // 返回一辆车(假设是报酬)

}

}

子类正确重写(Son.java)

public class Son extends Father {

// 【规则检查】:

// 1. 方法名、参数列表相同: (都是 cook(), 无参数)

// 2. 返回值类型是父类的子类(Porsche是Car的子类): (这叫协变返回类型)

// 3. 访问权限是public,和父类一样宽松:

// 4. 抛出的异常是父类异常的子类(BurntException是CookingException的子类):

@Override

public Porsche cook() throws BurntException {

System.out.println("儿子做了一盘米其林级别的西红柿炒鸡蛋!");

return new Porsche(); // 返回一辆保时捷(更好的报酬)

}

}

子类错误重写示例(会编译报错)

public class Son extends Father {

// 【错误示范】:

// 1. 返回值类型是毫不相关的MusicCD: 编译错误

// 2. 访问权限改为private,比父类的public更严格: 编译错误

// 3. 抛出的Exception是CookingException的父类,更宽泛: 编译错误

@Override

private MusicCD cook() throws Exception {

// ... ...

return new MusicCD();

}

}


四、如何标记重写?(@Override注解)

在Java中,强烈建议在重写的方法上加上@Override注解(Annotation)。

作用:

告诉编译器:“喂,编译器,我打算重写父类的一个方法,你帮我检查一下我的写法是否符合所有重写规则!” 如果不符合(比如你不小心把方法名写错了),编译器会立刻报错,帮你及时发现bug。

告诉读代码的人:“注意,这个方法是重写父类的,不是子类自己新定义的方法。”

一句话记重点:重写是子类把父类的同一个方法(同名同参)换了个更好的实现,但对外承诺(方法签名、返回值、异常等)不能变差,只能变好。

原文链接:,转发请注明来源!