多态是面向对象编程中的一个重要概念,它允许我们使用父类类型的引用来引用子类对象,并在运行时确定调用哪个子类的方法。这样可以提高代码的灵活性和可扩展性。在 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
覆写了父类Parent
的print
方法,并通过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
关键字可以提供代码安全性和稳定性。它告诉编译器和其他开发人员,该类、方法或变量的行为不应被改变。