多态
多态指的就是同一事物在不同情况下表现出来的不同状态或者行为。在java中,当调用同一个方法时,由于对象存在差异,不同的对象之间可能存在不同的行为,当父类引用指向了子类对象,用这个父类引用调用子类中重写的方法时,多态就出现了。
那么要如何理解多态的概念呢?这里说一个简单的例子,比如上班,将其理解为一个方法,这个方法在人这个类中有上班族这个对象来执行,但是不同的人的上班方式是不一样的,老板可能是开车上班的,打工人可能是挤地铁、坐公交或者骑共享单车上班的,还有可能是骑电瓶车上班的。这个对于同一个方法的不同实现方式就是指的多态,因此,在java中,多态只能是方法的多态,而不是属性的多态,多态与属性没有关系。
需要注意的是,多态只能在拥有方法重写的子类与父类中出现,并且必须要有父类引用指向子类对象。那么又如何理解父类引用指向子类对象呢,这里先编写一个程序来实现多态,用实际的例子来说明父类引用指向子类对象。
首先创建一个简单的Animal类,在这个类中定义一个shout方法,然后创建三个Animal类的子类,一个为Dog类,一个为Cat类,一个为Mouse类,在这前两个子类中重写shout方法,最后再定义一个测试类用来实现多态。以下为具体代码:
package cn.duotai.demo;import org.w3c.dom.ls.LSOutput;public class Animal {public void shout(){System.out.println("叫了一声!");}
}
class Dog extends Animal{public void shout(){System.out.println("汪汪汪!");}public void seeDoor(){System.out.println("看门!!");}}
class Cat extends Animal{public void shout(){System.out.println("喵喵喵!");}public void catchMouse(){System.out.println("抓老鼠!!");}
}
class Mouse extends Animal{public void shout() {System.out.println("叫了一声");}
}
package cn.duotai.demo;public class TestDuoTai {static void animalCry(Animal a){a.shout();}public static void main(String[] args) {Dog d = new Dog();animalCry(d);animalCry(new Cat());//测试类型转换Animal a= new Dog();//向上类型转换,自动类型转换//a.seeDoor();无法使用这种方式调用,编码事只能识别Animal,故调用时需要强制转型Dog d2 = (Dog)a;//强制转型,向下类型转换d2.seeDoor();//Cat c3 = (Cat)a;// c3.catchMouse();编译不会出错,但运行时会提示java.lang.ClassCastException:类型转换错误,狗不是猫if(a instanceof Dog){Dog d3 = (Dog)a;d3.seeDoor();}else if(a instanceof Cat){Cat cat = (Cat)a;cat.catchMouse();}}
}
在以上的代码之中,有一行为 Animal a = new Dog();在这个代码之中,a 为父类Animal的引用,而a指向的是Dog类的对象,这就是一个很明显的父类引用指向子类对象的例子。那么Dog d = new Dog;animalCry;这两行代码中有没有涉及到父类引用指向子类对象呢?答案是有,这里要注意的是animalCry(Animal a )这个方法中传入的参数是Animal类,于是在执行 Dog d = new Dog;animalCry;这两行代码时就有一个隐含的赋值操作,这个操作为 Anamal a = d = new Dog. 很明显形式参数充当了Animal类的引用,这个引用指向了Dog类的对象。下面一行的Cat类代码也是同一个原理。
对象的类型转换
在上面多态的例子中能够发现当实现父类的引用指向对象时,引用类型为Animal,而对象的类型为Dog。父类引用指向子类对象时,子类对象的类型应该要发生变化,变为Animal类的对象,也就是说,对象的类型能够发生转换。
那么对象的类型转换要遵循什么样的规则呢?这里的规则和之前的数据类型转换时一样的,向上转换回自动转换,而向下转换则需要进行强制转换。比如Dog类的对象要转换为Animal类,由于Animal类是Dog类的父类,因此转换会自动进行,但是将父类Animal对象转换为Dog类的对象时就要强制转换了,就像以上代码中的Dog d3 = (Dog)a;一样。
不过在这里要特别注意的一点是,同级别的对象之间能否相互转换呢?这里要说的是在编译上是允许的,但是在运行上是不允许的。也就是说,如果要进行同级别之间的对象转换,比如Dog类和Cat类都是Animal的直接子类,这时如果要将Dog类的对象强制转换为Cat类的对象,那么编译不会报错,但是运行时会报错,比如上面的代码中 //Cat c3 = (Cat)a; // c3.catchMouse();这两行在编译时时是不会报错的,但在运行时会提示对象不能进行强制转换。
那么这是为什么呢?这里需要理解一句话:在java中进行类的对象转换时遵循编译看左,运行看右的原则。在上面的程序之中Animal a = new Dog();编译时,只看代码的左边,此时a 是Animal类,Animal类进行强转,满足Dog is Animal 的逻辑条件,自然能转为Cat类,因此不会报错,但是在运行时,看的是右边,此时a 为Dog类,Dog类和Cat类是同级的,Cat is Dog这个条件肯定不满足,因此程序执行错误,无法进行对象的类型转换。