【骚全带你学Java---十三、面向对象之多肽】

多肽核心机制

1.面向对象最核心的机制——动态绑定,也叫多态
2.多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。这个跟iOS中的运行时runtime运行时具有类似的原理。
3.指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)
4.在继承链中对象方法的调用存在一个优先级:this.Display(object)、super.Display(object)、this.Display((super)object)、super.Display((super)object)。

多肽的定义

指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

多态存在的三个必要条件
  • 要有继承;
  • 要有重写;
  • 父类引用指向子类对象。

为什么要用多肽,好处是哪些?

我们都知道,一个事物的产生必然有其存在的道理,很多都是为了解决一些难题而产生,那么多肽具有哪些有点呢。

  • 1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
  • 2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
  • 3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
  • 4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
  • 5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

代码带你认识多肽

多态体现的格式:

父类类型 变量名 = new 子类对象;
变量名.方法名();

父类类型:指子类对象继承的父类类型,或者实现的父接口

Fu f = new Zi();
f.method();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。
  • 代码如下:
//定义父类
public abstract class Animal {
  public abstract void eat();
}
//定义子类
class Cat extends Animal {
  public void eat() {
  System.out.println("吃鱼");
 }
}
class Dog extends Animal {
  public void eat() {
  System.out.println("吃骨头");
 }
}
//定义测试类
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
  Animal a1 = new Cat();
// 调用的是 Cat 的 eat
  a1.eat();
// 多态形式,创建对象
   Animal a2 = new Dog();
// 调用的是 Dog 的 eat
   a2.eat();
 }
}

代码告诉你多肽的好处

实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。代码如下:

//定义父类
public abstract class Animal {
    public abstract void eat();
}
//定义子类
class Cat extends Animal {
   public void eat() {
   System.out.println("吃鱼");
  }
}
class Dog extends Animal {
   public void eat() {
   System.out.println("吃骨头");
  }
}
//定义测试类
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}

由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。
不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用showAnimalEat都可以完成。
所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展

引用类型转换

多态的转型分为向上转型与向下转型两种:

向上转型

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
    当父类引用指向一个子类对象时,便是向上转型。
    使用格式:

父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();

向下转型

  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的
    一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
    使用格式:

子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;

为什么要转型

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥
有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子
类特有的方法,必须做向下转型。

  • 转型演示,代码如下:
//定义类
abstract class Animal {
  abstract void eat();
}
class Cat extends Animal {
  public void eat() {
  System.out.println("吃鱼");
}
public void catchMouse() {
  System.out.println("抓老鼠");
 }
}
class Dog extends Animal {
  public void eat() {
  System.out.println("吃骨头");
 }
public void watchHouse() {
  System.out.println("看家");
 }
}

//定义测试类
public class Test {
  public static void main(String[] args) {
  // 向上转型
  Animal a = new Cat();
  a.eat(); // 调用的是 Cat 的 eat
  // 向下转型
  Cat c = (Cat)a;
  c.catchMouse(); // 调用的是 Cat 的 catchMouse
 }
}

转型的异常

转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

public class Test {
   public static void main(String[] args) {
   // 向上转型
  Animal a = new Cat();
  a.eat(); // 调用的是 Cat 的 eat
  // 向下转型
  Dog d = (Dog)a;
  d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
 }
}

这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

变量名 instanceof 数据类型
如果变量属于该数据类型,返回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
    }
 }
}

你可能感兴趣的:(【骚全带你学Java---十三、面向对象之多肽】)