• 通过实例聊聊Java中的多态


    Java中的多态允许父类指针指向子类实例。如:Father obj=new Child();  那么不禁要发问??

    使用这个父类型的指针访问类的属性或方法时,如果父类和子类都有这个名称的属性或方法,哪一个属性或方法会被调用呢?

    动手看看

     1 class Father
     2 {
     3     int r;
     4     Father()
     5     {
     6         r=4;
     7     }
     8     void printname()
     9     {
    10         System.out.println("I'm father");
    11     }
    12 }
    13 class Child extends Father
    14 {
    15     int r;
    16     Child()
    17     {
    18         r=5;
    19     }
    20     void printname()
    21     {
    22         System.out.println("I'm Child");
    23     }
    24 }
    25 public class Test
    26 {
    27     public static void main(String[] args)
    28     {
    29         Father obj=new Child();
    30         System.out.println(obj.r);
    31         obj.printname();
    32     }
    33 }

    测试结果:

    4
    I'm Child

    结果说明。属性会访问父类的。方法会访问子类的。

        不要以为Father obj=new Child();这条语句一定会让obj.printname()指向Chlid定义的printname()。实际上,如果你把Father类中的printname()方法删除,这个程序将编译失败。因为Father中的并没有定义printname()这个函数。 因为在Father obj=new Child()这个语句中,如果Father中没有printname()这个函数,就不会为obj建立一个用于调用printname()函数的指针。所以调用obj.printname()会出错。如果Father中有这个函数。指向printname()函数的指针会被创建。在调用obj.printname()时,不会出错,而且,因为obj指向的是new Child(),是个Chld类的实例。所以调用obj.printname()时调用了Child类中定义的printname()。这就是方法的动态绑定。

        那么,刚才说到把Father类中的printname()方法删掉后,obj将无法调用Child类中的printname(),因为obj.printname()会编译失败。那么如果我就是需要调用要怎么办呢?其实虽然obj是Father类型的,但是它指向的是一个Child类的实例。那么可以将obj强制类型转换为Child。再调用printname()方法就可以了。

    在上面程序中,把Father类中的printname()方法整个删掉,再将obj.printname() 改成 ((Child)obj).printname()后,

    编译成功,结果输出:

    4
    I'm Child

      不禁要思考 如何可以运行Child类中的printname()来输出“I'm Father”呢?

    我的办法  办法就是将Child类中定义的printname()整个删掉。为什么这样可以成功呢?自己想想,嘿嘿。最后会有个这样的思考题。

    好了说完方法,接下来说属性

           为什么obj.r是4?为什么不是5?

    呵呵。其实从哲学上讲很好理解,把自己想象为多态的设计者,如果属性还是指向子类的属性(已知方法是指向子类的方法了 ),那和new一个子类有什么区别,肯定是这个引用即可以用父类的“某些东西”,也可以用子类的“某些东西”,那才有”竞争力(存在意义)”。  留一个思考:为什么不反过来(指向父类的方法,子类的属性)?

    好了如下是正经解释:Java中的多态仅为方法而言,成员变量还是使用的父类的成员变量。也就是说,因为“Father obj =……”,所以obj是Father类型的,所以obj里面的r是Father里面的r,所以输出obj.r就是4了。

    能不能输出5?   如下 向下转型

    ((Child)obj).r

    那行 测试一下吧

    class Base {
            int i = 99;
            public void amethod() {
                    System.out.println("Base.amethod()");
            }
            Base() {
                    amethod();
            }
    }
    public class Derived extends Base {
            int i = 1;
            public static void main(String argv[]) {
                    Base b = new Derived();
                    System.out.println(b.i);
                    b.amethod();
           }
           public void amethod() {
                    System.out.println("Derived.amethod()");
           }
    }

    结果:

    Derived.amethod()
    99
    Derived.amethod()
    View Code

    这个程序的执行过程是这样的:
    第一行:Base b=new Derived();
    执行这一行语句的过程中,要构造Derived这个类,而它有父类Base,所以先构造Base类。构造Base类的默认构造函数有定义。内容是执行amethod()方法。
    实际上,Base类构造方法中的执行amethod(),相当于执行this.amethod(),在这个程序中,就相当于执行b.amethod()。而b是Base类型的,指向了Derived类的实例的指针。所以跟据上面我们的总结,实际上执行的是Derived类的amethod()函数。所以,第一行“Base b=new Derived();”执行完,输出"Derived.amethod()"。
    第二行:System.out.println(b.i);
    这个很简单,成员变量,不考虑多不多态,只看它定义时前面的类型。这个程序中是Base b,所以b.i就是Base类中的i。输出99
    第三行:b.amethod();
    调用Derived类中的amethod()方法。
    其实这行就是迷惑你的,如果没有这一行。你可能会警觉起来——咦?为什么这儿定义一个amethod()呢?没有地方调用它啊?
    有了这行代码,就会使你放松警惕。觉得,啊。定义了这个是用来让b.amethod();调用的。

    如果把Derived的amenthod删除 结果如何?

    Base.amethod()
    99
    Base.amethod()
    View Code
  • 相关阅读:
    CDR
    xshell鼠标文本设置
    .NET多线程学习之龟兔赛跑
    js传“+”或“&”到后台变空值的问题
    Java递归读取文件夹下所有文档
    破解Myeclipse10找不到Commonplugins文件夹
    Java下载文件
    根据给定日期判断上一旬的开始结束日期
    查看Tomcat内存并调整Tomcat内存
    Could not parse mapping document from input stream
  • 原文地址:https://www.cnblogs.com/smellpawn/p/10833739.html
Copyright © 2020-2023  润新知