Java编程思想——细话Java多态

多态是一项让程序员”将改变的事物与未变的事物分离开来“的技术。它可以消除类型之间的耦合关系,为程序员带来更快的程序开发过程,更好的代码组织,更好扩展的程序以及更容易的代码维护。


一、什么是多态

       多态不但可以改善代码的组织结构和可读性,还能够创建可扩展的程序(即无论在项目最初创建时还是在需要添加新功能时都可以”生长“的程序)。之所以可以进行扩展,是因为在多态中默认忘记了对象类型,比如类Shap,无论是Circle(圆形)、Square(正方形)还是Triangle(三角形)都可以说是一种Shape,也就是继承中常提到的”is-a“关系,此时如果继续添加一个Rhombus(菱形)类仍然满足这种关系,所以当进行向上转型时都可以说是一个Shape类,即忽视了具体类型,多态实例如下:

class Shape{
   public draw(){}
   public clear(){}
}
class Trangle extends Shape{
   //重写父类方法
   public draw(){
      System.out.print("Draw Trangle");
   }
   public clear(){
     System.out.print("Clear Trangle");
   }
}
class Circle extends Shape{
   //重写父类方法
   public draw(){
     System.out.print("Draw Circle ");
   }
   public clear(){
     System.out.print("Clear Circle ");
   }
}
//可以进行扩展Square、Rhombus……
public class Test{
   public static void main(String[] args){
     Shape[] shape = {new Trangle(),new Circle()};//多态,忽视Trangle和Circle的具体类型而一并看作为Shape类型
     for(Shape s:shape){
        s.draw();
        s.clear();
     }
  }
}
out:
Draw Trangle
Clear Trangle
Draw Circle
Clear Circle

二、实现机制——动态绑定

       从上面的例子中我们可以看到当引用s调用draw()和clear()方法时并不是调用基类的方法,而是调用Trangle和Circle中各自重写的方法,这正是我们想要的,但是它是如何实现的呢?实际上编译器是无法得知的,这归功于方法调用时的绑定

1.什么是绑定

   将一个方法调用同一个方法主体关联起来被称为绑定——即将Trangle和其重写基类的方法进行关联。

2.分类

   绑定分为前期绑定和后期绑定:

  • 前期绑定是指程序执行前进行绑定,由编译器和连接程序实现。(这是一种面向过程语言中的默认绑定方式
  • 后期绑定是指在运行时根据对象的类型进行绑定,所以也被称为动态绑定或运行时绑定

      要想进行后期绑定就必须要有某种机制来触发,而这种机制就是方法调用机制,实际上编译器是一直不知道对象类型的,但是方法调用机制能找到正确的方法体,并加以调用。现实当中,Java语言中除了static和final(包括private)方法之外,其他方法都是后期绑定,也就是说在Java中后期绑定是自动发生的。

3.为什么Java中的static、final、private修饰的方法都不是后期绑定

  • static方法——静态方法本属于类,所以其从创建后就与类关联。
  • final方法——此类方法是不能就行重写的,所以在调用过程中不会产生选择的歧义,所以不需要后期绑定。
  • private方法——此类方法实际上是属于final方法的,所以理由与final一致。

三、多态的缺陷

多态对于私有方法、域和静态方法是不起作用的,原因如下:

  • 私有方法:对于private方法实际是final类型的方法,所以其不能被覆盖,因此只能调用父类的私有方法。
  • :排除父类与子类同名域易混淆这一点不谈,实际上俩个域是拥有俩块独立内存的,因此域不存在可以被覆盖的问题,且域的访问是由编译器解析的,既然是编译器解析,所以不会是动态绑定,因为动态绑定是在执行时进行的,所以不是多态。
  • 静态方法:多态实际就是一种对象类型的向上转型,即将父类引用指向了子类对象,但是类本身不变,静态方法实际是与类直接关联的,而非单个的对象,所以静态方法不具有多态性

**补充一点**:

      既然有向上转型肯定就有向下转型,但是由于子类的方法不只包含所有的基类方法,还可能拥有基类没有的方法,所以在向下转型时很可能出现丢失方法的情况以致于转型失败,具体实例如下:

class Useful{
   public void f(){}
   public void g(){}
}
class MoreUserful extends Useful{
   public void f(){}//重写父类方法
   public void g(){}//重写父类方法
   public void u(){}//子类包含基类没有的方法
}
public class Test{
   public static void main(String[] args){
      Useful[] u={new Useful(),new MoreUseful()};  //多态(包含向上转型)
      u[0].f();
      u[1].g();
      //若要通过父类调用子类方法,需要向下转型
      (MoreUseful)u[0].u();//报错ClassCastException异常
      (MoreUseful)u[1].u();//向下转型成功!
   }
}

Java中所有转型都会进行检查,包括基本类型的强转。——运行时类型识别(RTTI)

多态中会遇到的异常——ClassCastException类转型异常 ,通常在向下转型时失败 。

猜你喜欢

转载自blog.csdn.net/goodli199309/article/details/79884155
今日推荐