Skip to content

多态是面向对象编程中的一个重要概念,它允许我们使用父类类型的引用来引用子类对象,并在运行时确定调用哪个子类的方法。这样可以提高代码的灵活性和可扩展性。在 Java 中,实现多态有两个关键点:继承和方法重写。

首先,需要有一个父类和至少一个子类。子类继承了父类的属性和方法,并且可以根据需要添加自己特定的属性和方法。

其次,子类可以重写override父类的方法,即在子类中重新定义与父类中具有相同名称和参数列表的方法。当通过父类类型的引用调用该方法时,程序会根据实际的对象类型来确定应该调用哪个版本的方法。

下面是一个简单的示例:

class Animal {
    public void makeSound() {
        System.out.println("Animal makes sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }

    public void fetch() {
        System.out.println("Dog fetches");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();  // 使用父类类型的引用指向子类对象
        animal.makeSound();  // 调用的是子类的方法

        // animal.fetch();  // 错误!无法调用子类独有的方法
        // 需要将animal强制转换为Dog类型才能调用fetch方法
        Dog dog = (Dog) animal;
        dog.fetch();  // 调用子类独有的方法
    }
}

在上面的示例中,Animal是父类,Dog是子类。我们通过使用Animal animal = new Dog()创建了一个Dog对象,并将其赋值给父类类型的引用animal。当我们调用animal.makeSound()时,实际上调用的是Dog类中重写的makeSound方法,输出结果为"Dog barks"

请注意,在这个示例中,我们无法直接使用animal来调用fetch方法,因为它是Dog类特有的方法。如果需要调用该方法,我们需要先将animal强制转换为Dog类型,然后才能调用fetch方法。 这就是Java中多态的基本概念和用法。多态使得代码更加灵活,可以根据具体的对象类型来决定执行哪个方法,提高了代码的可读性和可维护性。

覆写 Object 方法

在 Java 中,所有的类都直接或间接地继承自Object类。因此,每个类都会继承一些Objec类中定义的方法。有时候,我们需要覆写(重写)这些从Object类继承而来的方法,以便根据我们的需求进行定制化的实现。

以下是一些常见的Object类方法,可以被子类覆写:

  • toString():该方法返回一个包含对象信息的字符串表示。默认情况下,它返回对象的类名和哈希码。你可以根据需要覆写这个方法,以提供更有意义的字符串表示。
  • equals(Object obj):用于比较两个对象是否相等。默认情况下,它使用引用比较来判断对象是否相等。你可以覆写这个方法,以改变对象相等的逻辑。
  • hashCode():计算对象的哈希码。默认情况下,它返回对象的内存地址转换成整数。如果你覆写了equals()方法,通常也需要同时覆写hashCode()方法。
  • clone():用于创建当前对象的副本。默认情况下,它对对象执行浅拷贝。如果你想要实现深拷贝,就需要覆写这个方法。
  • finalize():该方法在垃圾回收器清理对象之前调用。你可以覆写这个方法,在对象被销毁之前执行一些清理操作。 这些方法是Object类中的几个常见示例,你可以根据需要覆写其他方法。请注意,在覆写方法时,要遵循相应的规则和约定,以确保正确的行为和语义。

调用 super 方法

在 Java 中,使用super关键字可以调用父类的方法或构造函数。super关键字有两种常见的用法: 调用父类的方法:在子类中,如果要调用父类中被覆写(重写)的方法,可以使用super关键字来引用父类的方法。例如:

class Parent {
    public void print() {
        System.out.println("Parent's print method");
    }
}

class Child extends Parent {
    @Override
    public void print() {
        super.print(); // 调用父类的print方法
        System.out.println("Child's print method");
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.print();
    }
}

输出结果:

Parent's print method
Child's print method

在上面的例子中,子类Child覆写了父类Parentprint方法,并通过super.print()调用了父类的print方法。

调用父类的构造函数:在子类的构造函数中,可以使用super关键字来调用父类的构造函数。这样可以在创建子类对象时先执行父类的初始化工作。例如:

class Parent {
    public Parent() {
        System.out.println("Parent's constructor");
    }
}

class Child extends Parent {
    public Child() {
        super(); // 调用父类的构造函数
        System.out.println("Child's constructor");
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
    }
}

输出结果:

Parent's constructor
Child's constructor

在上面的例子中,子类Child的构造函数通过super()调用了父类的构造函数。

需要注意的是,super 关键字只能在子类内部使用,并且必须放在方法或构造函数的第一行。这是因为在调用父类的方法或构造函数之前,必须先完成父类的初始化工作。

final 修饰方法

在 Java 中,final是一个关键字,用于表示不可变性。它可以应用于类、方法和变量。

final 修饰类

当我们使用final修饰一个类时,该类将被标记为最终类,即不能被其他类继承。这意味着无法创建该类的子类。例如:

final class FinalClass {
    // 类定义
}

// 以下代码会导致编译错误,因为FinalClass已经被声明为final,不能被继承
class SubClass extends FinalClass {
    // 子类定义
}
final修饰方法:当我们使用final修饰一个方法时,该方法不能被子类覆写或重写。这样做是为了防止子类修改父类的行为。例如:
class Parent {
    public final void finalMethod() {
        // 方法实现
    }
}

class Child extends Parent {
    // 编译错误,无法覆写finalMethod()
    // @Override
    // public void finalMethod() {
    //     // 子类方法实现
    // }
}

final 修饰变量

当我们使用final修饰一个变量时,该变量成为一个常量,一旦赋值后就不能再改变其值。通常,final变量的命名采用全大写的方式。例如:

class MyClass {
    public static final int MAX_COUNT = 100; // 常量声明

    public void myMethod() {
        final int localVar = 10; // 局部变量声明
        // ...
        // localVar = 20; // 编译错误,无法修改final变量的值
    }
}

使用final关键字可以提供代码安全性和稳定性。它告诉编译器和其他开发人员,该类、方法或变量的行为不应被改变。