多态是继封装、继承之后,面向对象的第三大特性。生活中,比如求面积的功能,圆、矩形、三角形实现起来是不一样的。跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态
定义
多态: 是指同一行为,对于不同的对象具有多个不同表现形式。程序中多态: 是指同一方法,对于不同的对象具有不同的实现.
前提条件
继承或者实现父类引用指向子类对象接口引用指向实现类对象方法的重写 实现多态
代码示例
class Animal{//父类
public void eat(){
System.out.println("吃东西");
}
}
//子类
class Dog extends Animal{
@Override
public void eat() {//方法重写
System.out.println("狗吃骨头");
}
}
//子类
class Cat extends Animal{
@Override
public void eat() {//方法重写
System.out.println("猫吃鱼");
}
}
public class Test {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal dog = new Dog();
dog.eat();//狗吃骨头
Animal cat = new Cat();
cat.eat();//猫吃鱼
}
}
编译时类型与运行时类型不一致问题
编译时,看“父类”,只能调用父类声明的方法,不能调用子类扩展的方法;运行时,看“子类”,一定是执行子类重写的方法体; 多态时访问成员的特点
查看下面代码,
class Fu {
int a = 10;
public static void method1() {
System.out.println("我是父类静态方法");
}
public void method2() {
System.out.println("我是父类非静态方法");
}
}
class Zi extends Fu {
int a = 20;
public static void method1() {
System.out.println("我是子类静态方法");
}
public void method2() {
System.out.println("我是子类非静态方法");
}
}
public class Test {
public static void main(String[] args) {
//多态
Fu Demo = new Zi();
System.out.println(Demo.a);//10
Demo.method1();//我是父类静态方法
Demo.method2();//我是子类非静态方法
}
}
多态时成员变量的访问特点
编译看左边,运行看左边:简而言之:多态的情况下,访问的是父类的成员变量
多态时成员方法的访问特点
非静态方法:编译的时候去父类中查找方法,运行的时候优先去子类中查找方法来执行静态方法:编译的时候去父类中查找方法,运行的时候去父类中查找方法来执行 多态的应用
如果变量的类型为父类类型,该变量就可以接收该父类类型的对象或者其所有子类对象
多态应用在形参实参:参数类型为父类类型,该参数就可以接收该父类类型的对象或者其所有子类对象
public class Test {
public static void main(String[] args) {
// 形参多态:参数类型为父类类型,该参数就可以接收该父类类型的对象或者其所有子类对象
Dog d = new Dog();
method(d);
System.out.println("===============================");
Cat c = new Cat();
method(c);
}
// 需求: 定义一个方法,带有一个参数,该参数可以接收Animal类对象以及Animal类的所有子类对象
// method(d); ====实参赋值给形参的时候==> Animal anl = new Dog();
// method(c); ====实参赋值给形参的时候==> Animal anl = new Cat();
public static void method(Animal anl){
anl.eat();
}
}
多态应用在返回值:如果返回值类型为父类类型,那么就可以返回该父类类型的对象或者其所有子类对象
public static Animal buy(String name){
if("猫咪".equals(name)){
return new Cat();
}else if("小狗".equals(name)){
return new Dog();
}
return null;
}
多态应用在数组:数组元素类型声明为父类类型,可以存储父类类型和其子类类型
Animal[] arr = new Animal[2]; //在堆中开辟了长度为5的数组空间,用来装Animal或它子类对象的地址 arr[0] = new Cat();//多态引用 左边arr[0] 是Animal类型,右边是new Cat() arr[1] = new Dog();多态的好处和弊端
好处:实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
弊端:多态的情况下,只能调用父类的共性内容,不能调用子类的特有内容。
class Animal{//父类
public void eat(){
System.out.println("吃东西");
}
}
//子类
class Cat extends Animal{
@Override
public void eat() {//方法重写
System.out.println("猫吃鱼");
}
public void catchFish(){
System.out.println("猫会抓鱼");
}
}
public class Test {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal cat = new Cat();
cat.eat();//猫吃鱼
//cat.catchFish(); 编译报错,因为多态成员访问的特点是,编译看父类,而父类中没有子类独有的功能
}
}
解决弊端的方式:
因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。但是,使用父类变量接收了子类对象之后,我们就不能调用子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换。
向上转型:当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型
此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了但是,运行时,仍然是对象本身的类型此时,一定是安全的,而且也是自动完成的
// 向上转型
Animal a = new Cat();
向下转型:当左边的变量的类型(子类)<右边对象/变量的类型(父类),我们就称为向下转型
此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了但是,运行时,仍然是对象本身的类型此时,不一定是安全的,需要使用(类型)进行强制类型转换不是所有通过编译的向下转型都是正确的,可能会发生ClassCastException,为了安全,可以通过isInstanceof关键字进行判断、
Aniaml anl = new Cat(); Cat c = (Cat)anl;//向下转型
为了避免ClassCastException的发生,Java提供了 instanceof`关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就一定是安全的,不会报ClassCastException异常。
执行流程:
判断前面变量指向的对象类型是否是后面的数据类型: 如果前面变量指向的对象类型是属于后面的数据类型,那么就返回true 如果前面变量指向的对象类型不是属于后面的数据类型,那么就返回false
所以,转换前,我们最好先做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
多态的应用场景综合案例
package demo07;
class Animal{
public void eat(){
System.out.println("吃东西...");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头...");
}
// 特有的功能
public void lookHome(){
System.out.println("狗在看家...");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼...");
}
// 特有的功能
public void catchMouse(){
System.out.println("猫抓老鼠...");
}
}
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
method(d);
System.out.println("==========================");
Cat c = new Cat();
method(c);
}
// 形参多态: 如果父类类型作为方法的形参类型,那么就可以接收该父类类型的对象或者其所有子类的对象
public static void method(Animal anl){
anl.eat();
if (anl instanceof Dog){
Dog d = (Dog)anl;// 向下转型 Dog类型
d.lookHome();
}
if (anl instanceof Cat){
Cat c = (Cat)anl;// 向下转型 Cat类型
c.catchMouse();
}
}
}
代码执行后结果



