了解Java的继承性和多态性
Java是一种面向对象的编程语言,因为它为Java开发者提供了将对象作为现实生活中的实体来实现的能力。OOP(面向对象编程)是一种围绕对象及其属性(属性)和行为(方法)建立的编程范式。所以,一般来说,使用面向对象原则的软件设计侧重于对象和操作。在面向对象编程的许多特点中,它鼓励代码的可重用性和可扩展性。我们将在后面详细解释这两个术语。但是,首先,本文将解释继承和多态性中的两个核心OOP原则。
前提条件
读者应具备以下条件
- 对Java编程有基本了解
- 在你的电脑上安装了Java
- 一个你选择的IDE
目标
在本教程结束时,读者应该明白。
- 什么是继承
- 多种继承方式
- 继承的层次结构
- 什么是多态性
- 多态性和继承的重要性
- 在Java中实现继承和多态性
什么是继承?
想象一下,设计一个类来创建Car ,如丰田、奔驰、劳斯莱斯等对象。所有的汽车对象都有相同的属性和行为。例如,所有的汽车都可以移动(驾驶)、鸣笛、加速,而且它们都有一个名字和发动机号码。与其单独创建这些类,不如用一个具有汽车所有属性和行为的一般汽车类(超类)来为所有汽车对象建模。然后,其他类继承或基于超类的属性和方法创建。简而言之,继承可以让你在其他类的基础上建立类,避免重复的代码。下面是一些代码来解释上面的图示。
// Declare a car superclass
public class Car {
private int speed;
private String name;
private String color;
private int enginePower;
public Car() {
}
public void accelerate() {
System.out.println("The car is accelerating…");
}
public void setSpeed(int speed) {
System.out.println("The car is " + speed);
}
public int getSpeed() {
return speed;
}
}
// Declare a sub car class
public class Toyota extends Car {
private int registrationNumber;
private int engineNumber;
public Toyota() {
// Toyota constructor calling the constructor of the superclass
super();
}
}
关键词extends 意味着Toyota 类继承于Car 类,并且是它的子类。丰田类既拥有Car 类的属性和方法,也拥有自己的属性和方法。这是继承的核心原则;它支持代码可重复使用的概念。下面的代码片段显示了Toyota 对象如何同时访问超类Car 中的属性和方法。
public class Main {
public static void main(String[] args) {
// This is the setSpeed() and accelerate() method in the Car class being accessed by the Toyota object.
Toyota myToyota = new Toyota();
myToyota.setSpeed(3);
myToyota.accelerate();
}
}
正确的说法是,子类是超类的一个专门版本。作为一个普遍接受的Java编程方法,在我们创建一个对象之前,它必须被声明为一个特定的类型:该对象是一个实例的类。继承通过多态替换原则改变了这一点。多态替代原则指出,在表达式中需要一个给定类型的对象时,它可以被一个给定类型的子类的对象所替代。下面是一个说明多态替换原则的代码片断。
Car myToyota = new Car();
// An object of type Car is assigned to a variable of type Car or
Car myToyota1 = new Toyota();
一个类型为Toyota 的对象被分配给一个类型为Car 的变量。这是有可能的,因为丰田是汽车的一个子类或子孙。不幸的是,多态替换原则在相反的方向上不能成立。因此,一个汽车对象不能被用来代替丰田对象。例如,下面的代码会导致编译错误。
Toyota myToyota = new Car();
/* Every Toyota is a Car but not every car is a Toyota */
现在是时候看看继承的类型了,但在这之前,让我明确一点。当超类中的一个方法在子类中被覆盖时,超类的方法在基类中可以使用super 关键字,后面是句号。下面的代码片段说明了这一点。
public class Toyota extends Car{
public void accelerate() {
super.accelerate();
System.out.println("Toyota is accelerating");
}
}
继承的类型
这些是继承的类型。
- 单一继承
- 多重继承
- 多级继承
- 分层继承
单一继承
在这种继承中,子类只继承单一父类的属性和方法。子类也可以在现有的代码中增加更多的功能。下面的例子说明了单继承。
public class SectionStudent {
// Declare your instance variables and methods here
}
public class SectionInheritance extends SectionStudent {
// Declare your instance variables and methods here
}
在单继承中,SectionInheritance 这个类只继承自SectionStudent 。
多重继承
在Java中,不允许多重继承,尽管可以通过接口实现。在Java中可以实现多个接口,但只能扩展一个类。
多级继承
在多级继承中,一个子类也作为另一个子类的基类。比如说,一个车辆类作为每个移动机器(包括汽车)的超类,而汽车也作为特定种类的汽车(如丰田、奔驰等)的超类。这就是多级继承的概念。下面的代码片断说明了这一点。
public class Vehicle {
// Do something
}
public class Car extends Vehicle {
// Do something
// At this point Car is a subclass of Vehicle
}
public class Benz extends Car {
// Here car is now a superclass
}
层级继承
这种类型的继承,其中有一个以上的类是基于一个超类的。例如,一个Benz 类继承于一个Car ,一个Toyota 类继承于一个Car ,这个列表继续。更简单地说,分层继承是指不同的类继承父类的那种继承。
public class Car {
// Write your methods here
}
public class Benz extends Car {
// Write your methods here
}
public class Porsche extends Car {
// Write your methods here
}
继承的注意事项
- 构造函数不被继承
- 一个类的私有成员不被继承
- 最终方法是继承的,但不能被重写
- 所有公有和受保护的成员和字段都被继承。
我们已经能够解释继承和继承的层次结构。现在让我们关注一下多态性。
多态性和它的实现
顾名思义,多态性是指可以采取多种形式或形状的能力。多态性是一个面向对象的编程概念,它允许你把共享同一超类的对象,无论是直接还是间接,都当作是超类的对象。例如,我们在继承中继承了超类的方法,而多态性则以不同的形式使用这些方法。
假设我们创建一个模仿动物运动的程序。例如,Dog 、Fish 、Bird 、Snake 等类,尽管它们都实现了超类animal中的move 方法,但它们的运动方式是不同的。下面的代码片段生动地说明了上面的说法。
public class Animal {
public void animalMove() {
System.out.println("Animal move");
}
}
public class Dog extends Animal{
public void animalMove() {
System.out.println("Dog is running");
}
}
public class Fish extends Animal{
public void animalMove() {
System.out.println("Fish is swimming");
}
}
public class Bird extends Animal{
public void animalMove() {
System.out.println("Bird is flying");
}
}
public class Snake extends Animal{
public void animalMove() {
System.out.println("Snake is crawling");
}
}
为了测试上述代码是否应用了多态性,让我们用主方法来测试。
public static void main(String[] args) {
Animal animal = new Animal();
animal.animalMove();
Animal dog = new Dog();
dog.animalMove();
Animal fish = new Fish();
fish.animalMove();
Animal bird = new Bird();
bird.animalMove();
Animal snake = new Snake();
snake.animalMove();
}
当我们运行这个应用程序时,下面的内容将在我们的控制台中打印出来。
Animal move
Dog is running
Fish is swimming
Bird is flying
Snake is crawling
多态性的类型
多态性有两种类型,下面列出了它们。
- 编译时多态性
- 运行时多态性
编译时多态性
当编译器遇到一个方法调用时,它会检查对象的类型,以确定它是否可以进行该调用。如果该类包含该方法或继承了该方法,程序就会被编译。编译时多态性的一个主要应用是Java的方法重载。
方法重载
这是一个概念,在这个概念中,方法被声明为相同的名称,但有不同的参数类型。该方法是在编译时确定的,因此名称中含有编译时的字样。下面是一个例子。
public class MathAddition {
public int sum(int a, int b, int c) {
return a + b + c;
}
public int sum(int x, int y) {
return x + y;
}
}
从上面的代码片断中,方法sum 被声明了两次。假设sum 被一个类型为MathAddition 的对象调用,你认为哪个会被调用?答案很简单,被调用的方法是由编译时传递的参数数量决定的,如下图所示。
public static void main(String []args){
MathAddition addition = new MathAddition();
System.out.println(addition.sum(3, 3, 3));
}
上述方法调用的结果将是9 。记住,我们有两个名称相同、参数不同的方法。但在调用时,仍然有三个参数被传入方法,以执行带有三个参数的方法。这就是编译时的多态性。
运行时多态性
这是一种发生在运行时的多态性。在运行时多态性中,两个名字相同的方法存在于两个不同的类中,而不像编译时多态性那样,两个方法存在于同一个类中。在运行时多态性中,一个类是父类,另一个是子类。在方法重写中可以看到运行时多态性的一个完美应用。
方法重写
这是一个概念,在父类中声明的方法在子类中被赋予不同的实现。在运行时,调用的方法是由被创建的对象决定的,而不是由引用类型决定的。这个代码片段说明了方法覆盖。
public class Animal {
public void animalRun() {
System.out.println("Animal is running");
}
}
public class Bird extends Animal {
public void animalRun() {
System.out.println(“Bird is running”);
}
}
public class Main {
public static void main(String[] args) {
// This will invoke the method in the Animal superclass
Animal newAnimal = new Animal();
newAnimal.animalRun();
// This will invoke the method in the Bird subclass
Animal birdAnimal = new Bird();
birdAnimal.animalRun();
}
}
在运行时,birdAnimal 对象将调用Bird 子类中的方法animalRun() ,因为创建的对象是Bird 类型。
现在我们已经详尽地讨论了继承、多态性和它们的类型,让我们继续讨论它们的区别。
继承和多态性的区别
在继承中,我们创建的新类继承了超类的特征,而多态性决定了执行何种形式的方法。继承适用于类,而多态性适用于方法。
多态性和继承的重要性
以下是多态性和继承是OOP基本概念的几个原因。
- 继承鼓励类的层次结构
- 继承鼓励代码的重用性
- 多态性使之简单化
- 多态性鼓励代码的可扩展性
总结
在这篇文章中,我们已经了解了继承和多态性的概念。我们谈到了继承和多态的类型以及它们的实现。我们还谈到了它们的区别。