多态是同一个行为具有多个不同表现形式或形态的能力。
多态性是指父类的某个方法被其子类重写时,可以各自产生自己的功能行为
介绍多态之前,先介绍一下上转型。上转型(Upcasting)和多态(Polymorphism)之间存在密切的关系,它们是实现Java中多态性的关键机制之一。
假设A类是B类的父类,当用子类创建一个对象,并把这个对象的引用放到父类的对象中,例如:
A a;
a=new B();
或
A a;
B b=new B();
a=b;
?这时,称对象a是对象b的上转型对象。
以下是上转型对象示意图
上转型对象不能操作子类新增的成员变量(失掉了这部分属性),不能调用子类新增的方法(失掉了一些功能)。这是因为上转型时,编译器只能看到父类的成员变量和方法,而不知道具体是哪个子类的实例。
上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的方法。上转型对象操作子类继承的方法或子类重写的方法,其作用等价于子类对象去调用这些方法。因此,如果子类重写了父类的某个方法,当对象的上转型对象调用这个方法时一定是调用了子类重写的方法
class Animal {
void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
private String breed; // 子类新增的成员变量
Dog(String breed) {
this.breed = breed;
}
void fetch() {
System.out.println("狗接飞盘");
}
String getBreed() {
return breed;
}
}
public class Test {
public static void main(String[] args) {
// 上转型
Animal myDog = new Dog("Labrador");
// 不能直接访问子类新增的成员变量
// System.out.println(myDog.breed); // 编译错误
// 不能调用子类新增的方法
// myDog.fetch(); // 编译错误
// 访问父类的成员变量和调用父类的方法
myDog.makeSound(); // 输出:动物发出声音
// 如果需要使用子类新增的功能,需要进行下转型,使用 instanceof 进行类型检查
if (myDog instanceof Dog) {
Dog myActualDog = (Dog) myDog;
System.out.println("Breed: " + myActualDog.getBreed()); // 输出:Labrador
myActualDog.fetch(); // 输出:狗接飞盘
}
}
}
说明:
在这个例子中,首先创建了一个 Dog 类,其中包含了子类新增的成员变量和方法。然后,在 main 方法中,我们创建了一个 Dog 类型的对象,并使用上转型将其赋值给 Animal 类型的引用变量 myDog。接着,我们访问了父类 Animal 的成员变量和方法,然后使用 instanceof 进行类型检查,并进行下转型,以访问子类 Dog 特有的成员变量和方法。
下转型(Downcasting)是从一个更抽象的类型转换为一个更具体的类型。在Java中,下转型通常是将一个父类类型的引用转换为一个子类类型的引用。这在使用上转型时会很有用,因为上转型将子类对象赋给父类引用,而在某些情况下,我们可能需要恢复到原始的子类类型以访问子类特有的成员变量和方法。需要注意的是,进行下转型时,如果对象的实际类型不是我们期望的类型,会导致 ClassCastException 异常。因此,建议在进行下转型之前先使用 instanceof 进行类型检查。
当一个类有很多子类,并且这些子类都重写了父类中的某个方法时,把子类创建的对象的引用放到一个父类的对象中,就得到了该对象的一个上转型对象。这个上转型对象在调用这个方法时可能具有多种形态,因为不同的子类在重写父类的方法时可能产生不同的行为。
多态性在面向对象编程中具有多方面的优点,它提高了代码的灵活性、可维护性和可扩展性。以下是多态性的一些主要优点:
代码复用: 多态性允许使用相同的接口或基类引用指向不同的子类对象,从而实现了代码的复用。通过共享相同的接口,可以更容易地管理和扩展代码。
可替代性和可扩展性: 新的子类可以被创建而不会对已有的代码产生影响。已有的代码可以通过引入新的子类实现功能的扩展,而不需要修改已有的代码。
简化代码结构: 多态性可以使代码结构更加简洁。通过使用基类类型引用指向不同的派生类对象,可以减少条件语句的使用,提高代码的可读性。
接口和抽象类的应用: 多态性常常与接口和抽象类一起使用,使得程序更易于理解和维护。接口和抽象类定义了一套规范,而多态性则实现了这些规范。
解耦合: 多态性有助于降低类之间的耦合度。基类引用指向派生类对象,代码更关注于接口和行为,而不是具体的实现细节,使得代码更加灵活和易于维护。
可替代性和可扩展性: 通过多态性,可以在不改变现有代码的情况下引入新的类,实现对系统的扩展。这种可替代性和可扩展性是面向对象编程中重要的设计原则之一。
总体而言,多态性使得代码更容易理解、修改和扩展,提高了代码的质量和可维护性,是面向对象编程中强大的设计机制。
class Animal {
void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
void makeSound() {
System.out.println("汪汪");
}
}
class Cat extends Animal {
void makeSound() {
System.out.println("喵喵");
}
}
public class Test {
public static void main(String[] args) {
Animal myAnimal; // 父类引用
myAnimal = new Dog(); // 子类对象赋给父类引用,上转型
myAnimal.makeSound(); // 输出:汪汪
myAnimal = new Cat(); // 另一个子类对象赋给父类引用,上转型
myAnimal.makeSound(); // 输出:喵喵
}
}
说明:
在 main 方法中,我们首先创建了一个 Animal 类型的引用变量 myAnimal。然后,通过上转型,我们将一个 Dog 类型的对象赋值给 myAnimal,接着调用 makeSound() 方法。
这里的关键是,在运行时,myAnimal 实际上指向了一个 Dog 类型的对象,因此调用 makeSound() 方法时,执行的是 Dog 类中重写的方法,输出了 "汪汪"。同样的过程再次发生,当我们将一个 Cat 类型的对象赋值给 myAnimal 时,myAnimal 指向了一个 Cat 类型的对象,调用 makeSound() 方法时执行的是 Cat 类中重写的方法,输出了 "喵喵"。
这种通过父类引用指向不同子类对象,并调用相同方法表现出不同行为的特性就是多态性。在这个例子中,相同的 makeSound() 方法根据实际类型的不同而表现出不同的行为。
在Java中,多态性是一种强大的面向对象编程概念,使得同一类型的对象在不同上下文中表现出不同行为。其中,上转型是多态性的一种具体实现方式,通过将子类对象赋值给父类类型的引用,实现了在同一类型的引用下调用不同子类的方法。这种灵活性使得代码更容易扩展和维护,同时提高了代码的可复用性。通过上转型,我们可以处理不同类型的对象,而具体执行哪个子类中的方法由对象的实际类型决定,从而展现了多态性的核心思想。
学习Java多态为我带来了更加灵活和可扩展的编程技能。通过理解和应用上转型,我能够使用父类引用处理不同子类的对象,使代码更加通用和易于维护。这种特性不仅提高了代码的可读性,还增强了程序的可扩展性,使我能够轻松地适应未来需求的变化。通过多态性,我可以更有效地利用继承和重写,实现代码的高度复用,减少了冗余代码的编写。总体而言,学习Java多态性不仅拓展了我的面向对象编程思维,还提升了代码设计的水平,使我能够更好地构建可维护且高效的软件系统。