目录
何为多态
多态的实现条件
多态特点
继承关系:
方法重写:
父类引用指向子类对象:
运行时类型确定方法调用:
重写
概念:
注意事项:
向下转型和向上转型
何为多态
多态的概念:通俗来说,就是多种形态,具体来说,就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
如:打印东西,彩色打印机打印出来的是彩色的,黑白打印机打印出来的是黑白的。
总的来说:同一件事情,发生在不同对象身上,就会产生不同的结果。
多态的实现条件
在Java中,要实现多态性,需要满足以下条件:
继承关系:多态性通常是通过父类和子类之间的继承关系实现的。子类可以继承父类的方法,并且可以重写(覆盖)父类的方法。
方法重写:子类重写(覆盖)父类的方法,即在子类中定义一个与父类相同签名的方法。当使用父类引用指向子类对象时,调用该方法会执行子类中的实现。
父类引用指向子类对象:利用父类的引用变量指向子类的对象,通过这种方式可以实现多态性。编译时类型是父类,而运行时类型是子类。
运行时类型确定方法调用:在运行时,根据对象的实际类型确定调用的方法。即使引用变量的编译时类型是父类,但实际上调用的是子类中重写的方法。
多态特点
以下是结合具体代码示例,体现每个多态特点的例子:
继承关系:
class Animal {public void eat() {System.out.println("Animal is eating");} }class Dog extends Animal {@Overridepublic void eat() {System.out.println("Dog is eating");} }
在这个示例中,Dog类继承自Animal类,形成了继承关系。
方法重写:
class Shape {public void draw() {System.out.println("Drawing a shape");} }class Circle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a circle");} }
在这个示例中,Circle类重写了Shape类中的()draw()方法。
父类引用指向子类对象:
Shape shape = new Circle();
这行代码中,shape是Shape类的引用,但指向了Circle类的对象,实现了父类引用指向子类对象的特点。
运行时类型确定方法调用:
Shape shape = new Circle(); shape.draw();
在这个示例中,即使shape的编译时类型是Shape,但在运行时确定调用的是Circle类中重写的()draw()方法。
重写
概念:
当子类重写(覆盖)父类的方法时,子类可以提供自己的实现,从而改变父类方法的行为。这是多态性的一个重要概念。以下是一个示例代码,演示了重写方法时多态性的应用:
class Animal {public void makeSound() {System.out.println("Animal makes a sound");} }class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks");} }class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("Cat meows");} }public class Main {public static void main(String[] args) {Animal animal1 = new Dog();Animal animal2 = new Cat();animal1.makeSound(); // 输出:Dog barksanimal2.makeSound(); // 输出:Cat meows} }
注意事项:
- 方法签名必须一致:
在重写方法时,子类的方法签名(方法名称、参数列表和返回类型)必须与父类中被重写的方法完全一致。否则,编译器将视其为新的方法而不是重写方法。
- 访问修饰符:
子类中重写的方法的访问修饰符不能比父类中被重写方法的访问修饰符更严格。例如,父类中的方法是public,则子类中重写的方法不能是protected或private。
- 异常:
子类中重写的方法不能抛出比父类中被重写方法更宽泛的异常。如果父类方法抛出异常,子类重写的方法可以不抛出异常,但不能抛出更宽泛的异常。
- super关键字:
在子类中重写父类方法时,可以使用super关键字调用父类的被重写方法,以便在子类中添加额外的功能而不完全覆盖父类方法。
- final方法:
不能重写父类中被声明为final的方法。final方法表示该方法不能被子类重写。
- 静态方法:
静态方法不能被重写,因为静态方法与类相关联,而不是与对象相关联。子类中可以定义与父类中静态方法同名的静态方法,但不会构成重写。
- 构造方法:
构造方法不能被重写,因为构造方法是用于创建对象的特殊方法,而不是普通的成员方法。
向下转型和向上转型
假设我们有以下的类结构:
class Animal {public void makeSound() {System.out.println("Animal makes a sound");} }class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks");}public void fetch() {System.out.println("Dog fetches a ball");} }
现在我们来演示向上转型和向下转型:
public class Main {public static void main(String[] args) {// 向上转型Animal animal = new Dog(); // 将子类对象赋值给父类引用animal.makeSound(); // 输出:Dog barks// animal.fetch(); // 编译错误,Animal 类型没有 fetch() 方法// 向下转型if (animal instanceof Dog) {Dog dog = (Dog) animal; // 将父类引用强制转换为子类引用dog.fetch(); // 输出:Dog fetches a ball}} }
在上面的示例中,我们首先进行了向上转型,将一个Dog对象赋值给一个Animal引用。虽然我们使用的是Animal引用,但实际上调用的是Dog类中重写的makeSound()方法。在向上转型后,我们无法直接调用Dog类特有的方法fetch(),因为Animal类型没有这个方法。
然后,我们进行了向下转型。在进行向下转型之前,我们使用instanceof关键字来检查对象是否是Dog类的实例,以避免ClassCastException异常。如果对象是Dog类的实例,我们就将Animal引用强制转换为Dog引用,这样就可以调用Dog类特有的方法fetch()。