理解Java多态

面向对象编程有三大特性:封装、继承、多态。

      封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。

      继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。,同时继承也为实现多态做了铺垫。

       那么什么是多态呢?在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许父类型的引用指向子类型的实例对象。因为存在类型转换机制,从而造成无法在编译时识别一个引用表达式所指向的实例对象是该引用表达式的类型的实例对象,还是其子类型的实例对象,所以在动态多态性的方法调用中无法在编译时识别具体调用的成员方法,而这一般需要在运行时才能被Java虚拟机识别,因此称之为动态多态性。

       由此,我们也可以看出,动态多态性离不开继承(对于接口来说是实现)。在继承中,我们知道向上转型,但是向上转型会导致一些方法和属性的丢失,我们举个例子,定义一个父类Animal,子类Cat。Animal有call的方法,Cat中重写了call这个方法,Cat有属于自己的scratch行为,代码如下:

class Animal {

    public void call(){
        System.out.println("动物叫");
    }

}
class Cat extends Animal{
    
    public void call(){
        System.out.println("动物猫叫");
    }
    public void scratch(){
        System.out.println("动物猫挠人");
    }
}

public class AnimalTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Animal a = new Cat();
        a.call();
//        a.scratch();  错误,因为Cat()的向上转换,造成只属于Cat()中方法的丢失。
        
    }

}

   运行结果:

动物猫叫

 

       我们这样理解:

  1.   这里定义了一个Animal类型的a,它指向Cat对象实例,由于Cat继承于Animal,所以Cat可以自动向上转型为Animal,所以a是可以指向Cat实例对象的。
  2.   由于Cat中重写了父类Animal的call方法,程序运行时Java虚拟机识别出a指向的实例对象是Cat,因此a.call()实际调用的是Cat中的call()方法。
  3.   由于Animal中不存在scratch方法,所以子类Cat在向上转换时会丢失scratch方法(“丢失”我觉得并不准确,实际上在实例化Cat后,scratch方法是一直存在的,只不过向上转型后对于Animal的引用来说scratch隐藏了起来),因此a.scratch是有误的。

      实现多态有三个必要条件:继承、重写、向上转型,向上转型造成了子类中一些方法的丢失。 接下来,我们看个经典的例子:

public class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    } 

}

public class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    } 
}

public class C extends B{

}

public class D extends B{

}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));      
    }
}

  运行结果:

 

1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D

 

  上面程序中我们可以看出A、B、C、D存在如下关系

 

  我们把静态多态性看做是不同的方法,类A有两种方法,表示为A:show(D);A:show(A);

  类B继承于A,所以类B继承了类A的两种方法,表示为A:show(D);A:show(A);同时,类B有自己的方法,表示为B:show(B);B:show(A);这里B中重写了类A的show(A)方法,因此,B中的方法最终为A:show(D);B:show(B);B:show(A);

  类C,类D继承于B,所以类C、类D的方法为:A:show(D);B:show(B);B:show(A);

  

  接下来,我们分析变量:

  A a1 = new A(); a1拥有类A的两种方法。A:show(D);A:show(A);

  A a2 = new B(); a2指向实例对象B,因此a2有类B的三种方法,又因为类B的向上转换,导致只保留类B中与类A中相同的方法,因此a2中的方法为A:show(D);B:show(A);

  B b = new B(); b拥有类B的三种方法A:show(D);B:show(B);B:show(A);

  C c = new C(); c拥有类C的三种方法A:show(D);B:show(B);B:show(A);

  D d = new D(); d拥有类D的三种方法A:show(D);B:show(B);B:show(A);

 

  接着,我们分析方法:

  a1.show(b):b是类型B的引用,a1不含有参数为类型B的方法,b可以向上转型为A,调用A:show(A);

  a1.show(c)同上;

  a1.show(d):d是类型D的引用,a1中有方法show(D),可以直接调用A:show(D)

  a2.show(b):b是类型B的引用,a2中不含有参数为类型B的方,b可以向上转型为A,调用B:show(A);

  a2.show(c)同上

  a2.show(d):a2中有方法show(D),可以直接调用A:show(d)

  b.show(b):b中有方法show(B),可以直接调用B:show(B);

  b.show(c):c是类型为C的引用,b中没有方法show(C),但是c可以向上转型为B,调用show(B);转型的时候,先转为直接父类,若没有找到直接父类对应的参数,再继续向上转;

  b.show(d):b中有方法show(D),可以直接调用A:show(D);


  后记:以上是本人对多态继承的一些理解,刚开始接触Java,若有不对,恳请指正,虚心学习。

 参考:
http://www.cnblogs.com/chenssy/p/3372798.html
Java程序设计教程
https://baike.baidu.com/item/%E5%A4%9A%E6%80%81/2282489?fr=aladdin
 
 
 
 

 

 

posted @ 2017-10-07 10:08  布拉格的小调  阅读(164)  评论(0编辑  收藏  举报