自己挖的坑,再怎么说也要填上,终于到了三大特性最后一个——多态啦。
所谓的多态,顾名思义,就是一个物体的多种形态(有种很玄幻的感觉)。在代码中就是某个对象的编译时类型和运行时类型不同,那么就出现多态了。那么什么是编译时类型和运行时类型呢,想要了解多态我们就要先搞清楚这两个之间的不同。
编译时类型:声明该变量时使用的类型
运行时类型:实际赋给该变量的类型
那么多态究竟是干嘛的呢,我们用代码来理解
class Animal {
String name = "animal";
public void run() {
System.out.println("父类的run方法");
}
public void AAA() {
System.out.println("父类的AAA()方法");
}
}
public class Tiger extends Animal{
String name = "tiger";
@Override
public void run() {
System.out.println("子类的run方法");
}
public void eat() {
System.out.println("吃肉");
}
public static void main(String[] args) {
Animal aaa = new Animal();
System.out.println(aaa.name);
aaa.run();
aaa.AAA();
System.out.println();
Tiger ttt = new Tiger();
System.out.println(ttt.name);
ttt.run();
ttt.eat();
ttt.AAA();
System.out.println();
Animal xxx = new Tiger(); //这里的编译时类型和运行时类型不同,出现多态
System.out.println(xxx.name);
xxx.run();
xxx.AAA();
}
}
这里我们创建了一个Animal的父类,接着用Tiger继承了该类,接着在Tiger里创建了三个变量,前面两个变量是我们常见的变量,而第三个变量则是出现了多态。
这里我们可以看出多态会出现的场合,首先编译型类型(左边)是父类,运行时类型(右边)是子类,即多态出现在有继承关系的类中且一个父类的变量引用了子类对象的地址,另外子类要重写父类的方法。
接着我们看看运行结果
这里我们可以看到前面两个变量的方法都正常,第三个产生多态的变量就出问题了,明明他的变量类型是父类的类型,可是返回的变量和方法都是子类重写后的方法——除了子类中没有的AAA()方法。这里就直接放结果了,产生了多态的变量就能调用父类的方法和实例变量,而实际上运行的是子类重写的方法,注意,不能调用子类的特殊方法(没有重写父类的方法),因为他的变量类型还是父类的,因此编译时无法调用子类的特殊方法(没有重写父类的方法)。还有一点就是实例变量不具有多态性。
那如果我们就是想用子类的方法怎么办?这是我们就可以用到类型转换了。我们可以使用类型转换来强制将父类类型的变量转化为子类类型的变量,这样就能欧使用子类特有的方法了。语法如下:
Tiger xxx2 = (Tiger) xxx;
说了这么多这么绕的东西,那么最重要的一个问题来了,多态究竟有什么用?这个我们在讲继承的时候就说过了,就是为了代码的复用(偷懒),也为了程序的扩展,具体做法刚才也说了,就是设置一个父类类型的变量,接着将子类的对象引用赋给他。
以后我们想改就直接修改子类的引用(“=”右边的),而不用修改变量的类型,就如我们刚才的代码,如果我们有一个另外的类调用了刚才的xxx对象后需求改了,要将这个对象改成一个兔子类的对象,如果我们不使用多态,那么就要将整个对象都修改了,用了多态之后只需变动右边的对象引用而不用修改变量的类型。
哎,语言表达好像不太行,大家先别溜,等我上段代码:
这两段代码我们可以看到,如果我们没用多态(第一张图),那么我们就要改掉红框里的两行代码,如果我们用了很多个方法来调用的话,那么我们修改的代码量会成倍增加。若果我们使用了多态,就只用改主方法里红框的右边(也就是换一个子类对象的引用)就行了,我们可以这么说,所有这些东西都是为了方便代码的维护(也就是偷懒,偷懒是人类进步的阶梯!)。
其实这里还是有一个问题的,如果你运行了这段代码,就会发现,我们获取名字时显示的会是Animal设置的名字,如果你想要使用子类的变量时就需要进行类型转化了。而通过这里我们又可以知道,为什么说多态为了以后我们的程序的扩展做好了准备,因为以后我们想加什么狮子类,企鹅类只要继承Animal类然后就可以随意增加了,而不必加一个类就改一堆东西。