多态

1、多态的基本概念

1.1、概念理解

对于多态的基本概念网络上比较官方的说法是:多态是指允许不同类的对象对同一消息作出响应。

个人觉得这个说法对于初学者不太友好(笼统),因此通过学习之后个人的理解如下:

多态其实就是一个事物可以拥有多种状态也就是多种身份。举个简单的例子:我既是人也是学生,这就是两种状态,且两种状态存在继承,学生继承人的属性和方法。

1.2、多态体现

以上的概念只是从一个概念的角度说明了多态,还是比较浅显的,真正的理解应该是从其在代码中的体现进行说明的:

多态的代码体现在:

  • 一个子类对象既可以给这个子类对象的同类引用变量进行赋值。

例如:Person p = new Person(); p.study();

  • 一个子类也可以给这个子类的父类变量引用进行赋值。

例如 :Person p = new Student(); p.study;

注意:我们观察到上述的例子,特别是第二个例子中子类继承父类后,通过父类引用变量去指向子类对象。在编译阶段,编译器只会去核查Person类中是否有study函数,而并不关心study函数是否是父类的study还是子类的study,它只检查既然是p.study,那么就去person类中寻找study函数,如果没有就编译报错,编译报错后程序当然是无法运行的。但是在运行阶段时,执行的一定是Student类中的study函数,因为person p中的p是一个引用这个引用存放的是Student类的地址,程序运行时当然是通过地址进行运行的,因此此时的study函数一定是Student类中的study,而Student类中就算没有进行重写study函数,也会由于StudentPerson存在继承关系而会继承Person中的studyPerson中一定有study函数来给Student类继承,要不然编译不会通过。)此时的Study函数虽然是继承过来的,但是也是属于Student这个类了,而不是属于Person中的study。地址是谁的就是谁的函数。这就是对于多态的具体描述。其实就是可以实现在编译阶段和运行阶段绑定两种不同的状态。

例如:

//动物类
public class Bird extends Animal{
    public void move(){
        System.out.println("鸟儿在飞翔!");
    }
}

//猫类
public class Cat extends Animal{
   public void move(){
        System.out.println("猫在走!");
    }
    public void catchMouse(){
    	    System.out.println("猫抓老鼠!")
    }
}

//鸟类
public class Bird extends Animal{
    public void move(){
        System.out.println("鸟儿在飞翔!");
    }
    public void fly(){
    	   System.out.println("鸟飞");
    }
}
//测试类
public class AnimalTest {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.move();
					  //java中允许这个机制:父类引用指向子类对象。
        Animal c = new Cat();//发生了类型转换子类型转换成父类型,称为向上转型或者自动类型转换。
        c.move();
        c.catchMouse();//编译报错,这个方法不是重父类继承过来的,而是子类特有的,在编译阶段Animal.class字节				   //码文件中,没有catchMouse这个函数。
    }
}

(30、31行)注释:

  • 1、java程序分解为编译阶段和运行阶段。
  • 2、先分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的。
  • 3、编译阶段编译器检查c这个引用的数据类型为Animal,由于Animal.class字节码文件当中有move()方法,所以编译通过了。这个过程我们称为静态绑定/编译阶段绑定。只有静态绑定成功后才有后续的运行阶段。
  • 4、在程序运行阶段,JVM堆内存当中真实创建的对象是Cat对象,那么以下程序在运行阶段一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段绑定。(虽然Cat中的move函数也是继承过来的,继承过来了就属于它自身了。)
  • 5、无论是Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,因为底层真实对象就是Cat对象。
  • 6、父类引用指向子类对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,这种机制可以称为一种多态语法机制。

1.3、关于多态中涉及到的几个概念

  • 向上转型(upcasting):子类型–>父类型(自动类型转换)
  • 向下转型(downcasting):父类型–>子类型(强制类型转换,需要加强制类型转换符)

注意:

  • 无论是向上转型还是向下转型,两种类型之间必须要有继承关系。没有继承关系,程序是无法编译通过的。
  • 向上转型只要编译通过在运行阶段就不会出错,而运行异常只会存在于“向下转型”中。

若想让30/31行代码处的对象执行catchMouse()方法?

引用c是无法直接调用的,因为c的类型是Animal,Animal中没有catchMouse()方法。我们可以将c强制类型转换为Cat类型。c的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型。使用向下类型转换一定要存在继承关系。

什么时候要使用向下转型呢?

当调用的方法是子类型中特有的,在父类中不存在,必须进行向下转型。

例如:

Cat c1 = (Cat) c;
c1.catchMouse;//运行成功,猫抓老鼠

2、向下转型时的类型转换异常

Animal a1 = new Bird();
Cat c2 = (Cat)a1;

分析:

  • 以上程序编译时没有问题的,因为编译器检查到a1的数据类型是AnimalAnimalCat之间存在继承关系,并且Animal是父类型,Cat是子类型,父类型转换成子类型叫做向下转型,语法合格。
  • 程序虽然编译通过了,但是程序运行阶段会出现异常,因为JVM堆内存当中真实存在的对象是Bird类型,Bird对象无法转换成Cat对象,因为两种类型之间不存在在任何继承关系,此时出现了著名的异常:
    java.lang.ClassCasrException类型转换异常,这种异常只发生在“向下转型”的时候会发生。
  • java编程规范中要求:在进行向下类型转换时,建议采用instanceof运算符进行判断,避免出现转换错误。。

2.1、避免类型转换错误的方法

使用instsanceof运算符可以避免出现以上的异常

instanceof运算符怎么用?

语法格式:引用 instanceof 数据类型名

运算结果类型:布尔类型(true/false)

假设:(a instanceof Animal)

true表示:a这个引用指向的是一个Animal类型。

false表示:a这个引用指向的对象不是一个Animal类型。

例如:

if(a3 instanceof Cat)
{
     Cat = (Cat)a3;
     c3.catchMouse();
}
else if(a3 instanceof Bird)
{
	Bird b2 = (Bird)a3;
	b2.fly()
}

分析:这样能避免出现类型转换错误,只要引用指向的是什么变量才转换成什么变量。

3、多态的作用

  • 降低程序的耦合度,提高程序的扩展能力。
  • 父类型引用指向子类型对象。

实例

//猫类
public class Cat extends Pet {
    public void eat(){
        System.out.println("小猫在吃鱼!");
    }
}
//狗类
public class Dog extends Pet {
    public void eat(){
        System.out.println("小狗正在啃骨头!");
    }
}
//主人类
public class Master {
 //Master主人类面向的是一个抽象的宠物类,不再面向具体的宠物
 //提倡:面向抽象编程,不要面向具体编程。
    public void feed(Pet pet){
        pet.eat();
    }
}
//宠物类
public class Pet {
    public void eat(){
    }
}
//测试类
public class Test
{
    public static void main(String[] args) {
    //创建主人对象
        Master zhangSan = new Master();
    //创建猫对象
        Pet tom = new Cat();
        Pet erha = new Dog();

        zhangSan.feed(erha);
        zhangsan.fees(tom);

    }
}

分析:以上是一个使用多态语法机制后的例程,使用多态机制后,程序耦合度更低【解耦合】,扩展能力更强。其中之所以在抽象类Pet中写入一个空白的eat()函数,是为了后面继承的子类进行方法的重写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值