//下面我结合实例解释一下java中动态性实现问题 class base { public int ageOfBase=10; public void display() { System.out.println("display() in base!"); } public void display1() { System.out.println("display1() in base!"); } private void show() { System.out.println("show() in base!"); } } class test extends base { public int ageOfTest=10; /*这个函数改写父类方法 但是他并不是简单完成自己的功能; 在实现自己的应该实现的功能同时他还保留了父类的功能 ************************************* 重要的是如果你没有改写这个父类方法的话, 子类test还是存在这样的方法的,只是存在的方法实际上是从父类继承来的,只是简单的实现父类实现的功能。 */ public void display() { System.out.println("display() in test!"); super.display(); } //注意我并没有改写方法display1(),但是这个方法由于继承的原因 //子类test中还是存在的 //我写一个新的方法为子类 public void display2() { //这里引用父类的私有方法 //这个方法在子类中并不存在的 //但是一定要记住: //访问父类的私有成员是不允许的 //super.show(); //这里引用父类的共有方法 //实际上这里引用是子类中的方法 //因为这个方法已经从父类继承了 display1(); //下面这样引用也可以的 //这里是真正引用父类的方法的 // super.display1(); //上面这一点可能讲的不是很清楚的 //下面我换一种例子 //这个是子类的 display(); //这个是父类的方法 super.display(); System.out.println("display2()in test!"); } public static void main(String args[]) { test t=new test(); base b=new base(); //下面这一句是多态性的根本 //一个父类的引用指向子类 b=t; //这样通过父类的引用调用方法的话 //实际上是执行子类中实现的方法 /* 下面我结合我对面向对象编程的掌握和研究情况谈一下我的经验和看法其间我不能保证我说的每一点都正确,只是给你参考对于多态性这个面向对象编程领域很重要的概念。我结合c++以及针对java中的实际情况做几点说明。 (1)最好对于虚拟指针以及虚拟指针表格有一定了解。不了解的话,你只能是记住我说的,但是不一定能理解很好。 (2)父类引用指向子类。方法调用的时候执行的是子类的方法。这个说法是有很多前提和意外的。 下面我举一个例子 */ b.display(); // 下面调用会出错的 //t.display2(); //应该象下面那样 ((test)b).display2(); int temp1=b.ageOfBase; // 下面调用会出错 //int temp2=t.ageOfTest; //应该向下面那样 int temp2=((test)b).ageOfTest; /* 现在解释上面两个会出错的原因,这个原因是动态性的根本所在。 我尽力能解释清楚一点。 **************关键一******************** 因为父类和子类的对象在内存中的大小是不一样的,当然了不同的对象本身就不可能是同一块内存地址的。 这个应该没有问题的吧,当然了要是子类中只是简单的继承而没有都没有做的话,可能是一样的,但是在c++中各家编译器还有会有一点差异的,可能也会不一样的。 ***************关键二************************ 引用本质上是通过指针来实现的,更深入一点是引用是指向指针的指针,而不同类型的指针所管理的内存空间大小是不一样的,这一点跟上面联系很紧的。注意这里是说指针管理的内存而不是指针,我想大家都 知道指针只有4个字节长度的。这里我们可以简单的说是变量的可见性问题,因为变量虽然存在但是并不一定是可见的。 ***************关键三************************ 多态性的关键是动态绑定,说白了就是虚拟函数虚拟指针虚拟指针表等几个重要的概念。简单讲一下,这里很复杂的,但是每一个真正的面向对象编程人员必须花时间很好的理解这一点。java 中只有静态函数和虚拟函数两种,c++中还有一种 非静态非虚拟函数。所以java的动态绑定要容易一点的。 你可以不精确的这样理解(因为解释精确我也很懒,可以写很长的文章了),每次类编译的时候都会生成一个虚拟函数表,表里面保存自己类中所有的虚拟函数地址,同时要是这个类存在父类 (不管是extends class 还是implements interface)的话,同时他会把从父类继承来的虚拟函数表也继承过来(这里我只是大概解释,不是很准确)。 这个地方请注意: ************************************************ 要是子类没有改写父类继承的方法,父类虚拟函数中对应虚拟函数地址被变成子类自己的虚拟函数地址;如果没有改变则不需要改变继承来的虚拟函数表对应函数的地址。 ************************************************* ***************开始解释************************ 当父类引用指向子类对象的时候,父类引用管理的内存空间,是要比子类真实的内存空间小的。所以子类中比父类中多的东西对于这个父类引用来说是不可见的。所以也就没有办法引用。 上面的那个变量引用和函数引用就是这样的,因为父类中没有变量ageOfTest和函数display2()的。但是我们可以知道父类指针实际上是指向子类的,所以我们解决这个问题的方法就是:强制转换类型(java中叫造型)。 同时可能有人要问,子类中那个函数地址不是也在虚拟函数表中吗?很高兴你问这样的问题,但是父类引用看到的虚拟函数表是没有那个函数项目的,因为他对于父类引用绝对是不可见。 对于另外一个虚拟函数调用,如果你看了说明很仔细的话,你现在一定知道虽然是父类引用管理内存范围小,但是那个虚拟函数表,父类中有的,但是由于函数被子类中改写了,所以那个表中函数地址编程子类的拉,当然就是引用子类的方法了。 /****************重要补充************************ java中静态函数是没有动态性,因为静态函数不能被继承,也不能被改写为非静态方法。因为静态就意味着类的所有实例共享一个实体。 ************************************************** */ } } |