一、复写(@Override
)
作用:
主要用于指示一个方法是用来重写父类(包括父类是从接口继承而来的方法)中的方法,或者实现接口中的方法。它可以被看作是一种提示,帮助程序员在开发过程中更清晰地了解代码结构和方法的来源,也便于代码的维护和阅读。
使用场景示例:
当一个子类继承父类时,若子类中定义了一个与父类方法签名(包括方法名、参数列表等)完全相同的方法,此时就可以在子类的方法上使用 `@Override` 注解,表明这个方法是在重写父类中的对应方法。
* 比如:
class Parent{
public void display(){
System.out.println("Parent's display method");
}
}
class Child extends Parent{
@Override
public void display(){
System.out.println("Child's display method");
}
}
在上面的代码中,`Child` 类重写了 `Parent` 类的 `display()` 方法,并使用了 `@Override` 注解,这样在调用 `Child` 类对象的 `display()` 方法时,会执行子类中的这个重写后的方法。
当一个类实现接口时,实现接口中的方法也可以使用 `@Override` 注解来表明这是在实现接口的相应方法。不过从 Java 6 开始, `@Override` 注解就被允许用于实现接口方法的场景了
interface MyInterface{
void show();
}
class MyImpl implements MyInterface{
@Override
public void show(){
System.out.println("Implemented show method");
}
}
这里 `MyImpl` 类实现了 `MyInterface` 接口的 `show()` 方法,并通过 `@Override` 注解做出明确标识。
优点:
增强代码的可读性,让其他开发者快速了解这个方法的来源和用途。
有助于编译器进行检查。如果使用了 `@Override` 注解的方法实际上并没有正确地重写父类方法或者实现接口方法(比如方法签名不匹配等情况),编译器会报错,从而帮助开发者提前发现潜在的错误,避免一些因方法重写不当导致的难以察觉的逻辑错误。
二、继承(extends
)
以如下代码为例
public class MainActivity extends Activity
含义:
它表明 `MainActivity` 类是从 `Activity` 类继承而来的,或者说 `MainActivity` 是 `Activity` 类的一个子类。
具体解释:
public class MainActivity` 定义了一个名为 `MainActivity` 的公共类。
extends Activity` 表示这个 `MainActivity` 类要继承 `Activity` 类中定义的属性和方法。
继承的好处
代码复用:`MainActivity` 可以直接使用 `Activity` 类中已经定义好的各种方法和属性,无需重新编写相同的代码。比如,`Activity` 类中有用于管理界面生命周期(如 `onCreate()`、`onStart()`、`onResume()` 等)的方法,`MainActivity` 继承后就可以方便地重写和使用这些方法来控制自身的界面生命周期。
扩展功能:在继承的基础上,`MainActivity` 还可以添加自己特有的方法和属性,或者重写(通过 `@Override` 注解)从 `Activity` 类继承来的方法,以实现更符合自身需求的功能。
三、相关疑问
3.1 为什么要复写呢?新增一个方法,名字相同不就好了吗
在面向对象编程中,重写(Override)和新增同名方法是两个完全不同的概念,它们在语义和行为上有很大的区别。下面详细解释为什么需要重写而不是简单地新增一个同名方法:
1. 方法重写的本质:修改父类方法的行为
重写是子类对继承自父类的方法进行重新定义,目的是改变该方法在子类中的行为。子类通过重写可以提供与父类不同的实现逻辑,但仍然保持方法的签名(名称、参数列表等)一致。**示例:**
class Parent {
public void display() {
System.out.println("Parent's display");
}
}
class Child extends Parent {
@Override
public void display() {
System.out.println("Child's display");
}
}
在这个例子中,`Child` 类重写了 `Parent` 类的 `display()` 方法。当调用 `Child` 对象的 `display()` 方法时,执行的是子类中重写后的方法,而不是父类的方法。
关键点:
- **行为替换:** 重写的方法会完全替代父类中的同名方法。
- **多态性:** 通过重写可以实现多态性,即同一个方法名在不同类中有不同的实现。调用时根据对象的实际类型决定执行哪个方法。
2. 新增同名方法的问题:方法隐藏
如果子类中新增一个与父类方法同名但签名不同的方法(例如参数列表不同),这被称为方法隐藏(Method Hiding),而不是重写。**示例:**
class Parent {
public void display() {
System.out.println("Parent's display");
}
}
class Child extends Parent {
public void display(int a) { // 参数列表不同,不是重写
System.out.println("Child's display with argument");
}
}
在这个例子中,`Child` 类新增了一个与 `Parent` 类 `display()` 方法同名但参数列表不同的方法。这不会改变父类 `display()` 方法的行为,调用时需要根据参数匹配具体的方法。
问题:
- **行为不一致:** 如果希望通过子类对象调用父类的 `display()` 方法,但子类中没有重写该方法,仍然会执行父类的实现。
- **代码可读性差:** 新增同名方法容易让人误以为是重写,导致代码逻辑混乱。
3. 为什么要重写而不是新增同名方法?
(1)明确的行为替换
重写是为了明确地改变父类方法的行为。通过重写,子类可以提供更符合自身需求的实现逻辑,而不会影响父类的原有功能。
**示例:安卓开发中的 `onCreate()` 方法**
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在安卓开发中,`Activity` 类定义了 `onCreate()` 方法,子类通过重写该方法来初始化界面。如果不重写,父类的 `onCreate()` 方法将被调用,导致界面无法正确初始化。
(2)支持多态性
重写是实现多态性的基础。多态性允许通过父类引用调用子类的方法,这种机制是面向对象编程的核心之一。
示例:
Parent obj1 = new Parent();
Parent obj2 = new Child();
obj1.display(); // 调用 Parent 的 display()
obj2.display(); // 调用 Child 的 display()(多态)
如果子类没有重写 `display()` 方法,而是新增一个同名方法,`obj2.display()` 将无法调用子类的实现。
(3)遵循面向对象设计原则
重写符合面向对象编程中的“开闭原则”(对扩展开放,对修改关闭)。通过重写,子类可以在不修改父类代码的情况下扩展功能。
4. 总结:重写 vs 新增同名方法
特性 | 重写(Override) | 新增同名方法(Method Hiding) |
行为 | 替代父类方法的行为 | 隐藏父类方法,但不会改变其行为 |
参数列表 | 必须与父类方法完全一致 | 可以不同(如参数类型或数量不同) |
多态性支持 | 支持多态性,调用时根据对象实际类型决定方法 | 不支持多态性 |
代码可读性 | 清晰明确,表示行为替换 | 容易引起混淆,误以为是重写 |
结论:
重写是为了明确地改变父类方法的行为,并支持多态性。而新增同名方法(方法隐藏)不会改变父类方法的行为,且容易导致代码逻辑混乱。因此,在需要修改父类方法行为时,应该使用重写而不是简单地新增同名方法。