多态的必要条件,静态绑定与动态绑定在实际代码中如何区分?
问题描述:
在3-2中,课中提到,多态的必要条件是
首先要满足继承关系,其次需要满足父类引用指向子类对象。
Animal two = new Cat();
问题1:在案例中猫类有个run,而父类没有,这时候调用two.run();会出现编译错误。于是我就想,这个必要条件是不是还要有父类与子类需要存在被调用的方法的重写?
问题2:3-3中的静态绑定例子,当变量类型是与对象本身类型一致的时候,jvm也会先在子类里寻找方法,如果没有重写,就会去父类找匹配的方法。这不也是动态绑定么?还是说,动态绑定只会发生在编译型类型与运行时类型不一致的情况?网上搜索的一些资料说“java当中的方法只有final,static,private和构造方法是前期绑定”(java编程思想)那编译类型和运行时类型一致的时候,如3-3的静态绑定例子,调用普通方法到底时静态绑定还是动态绑定呢?
问题3:静态绑定和动态绑定很抽象,到底如何理解?还是说只能记住final static private 构造方法是静态绑定,只要是方法需要根据具体对象类型判断就是动态绑定(那不就是只要new xxx就是动态...)?
尝试过的解决方式:
对于问题1:
我之所以这么想是因为我不太清楚多态在编程中是单单指 父类引用指向子类对象 也就是能完成
Animal two = new Cat();这一行为就算多态,还是说多态是Animal two = new Cat();(向上转型)+ two.eat(); 或 two.run();(动态绑定:寻找转型前的类型并绑定正确的方法)这两个行为的结合?如果也包括two.run(); 那么必要条件就得有 被调用方法在父子类中需要存在重写关系。
对于问题2:
public void unique() { System.out.println("这是个没有被子类重写的方法"); }
然后在测试中
这个unique是没有被子类重写的。在这里,我没有运行程序,eclipse也显示了这个方法是来自Animal了,这就是程序还没运行,处于编译状态吧?这样看,它确实在编译状态就已经确定方法与类了,所以符合静态绑定的描述。但是我找的资料说成员方法支持动态绑定,也没说清楚调用普通成员方法是属于静态还是动态。我看过的所有资料都提到"前面已经说了对于java当中的方法而言,除了final,static,private和构造方法是前期绑定外,其他的方法全部为动态绑定。" 所以我已迷糊了。
正在回答
同学你好~
问题一:
这里“子类需要重写父类方法”并不是必要条件,因为多态的本质是“编译期检查引用类型,运行期执行实际类型”,而即使子类不重写父类方法,虚拟机也会先去子类中进行方法寻找,然后再去父类。这里只要有“先去寻找”的动作就已经满足多态了,只不过如果没有重写方法,在运行效果中无法直观的体现多态。
问题二:
首先要明确“动态绑定”和“静态绑定”的定义和条件:
动态绑定:指在运行期,方法和属性与类型间的关联动作。动态绑定通常是运行期根据实际类型进行运行,此时与引用类型已经没有本质关联。
静态绑定:指在编译期,方法和属性与类型间的关联动作。静态绑定必须满足在编译期已经对成员进行确定,并不会在运行时进行更改。
同学的疑问中“当变量类型是与对象本身类型一致的时候”,此时对于编译期来讲,已经确定了会使用子类的成员,只不过该成员是从父类继承的。如上一个问题所说,这里也是先去子类中进行寻找,只要有“先去寻找”的动作已经满足条件了。
对于同学查询的资料,说法是正确的,老师分别进行分析:
1.final修饰的方法:被final修饰的方法重要的特点是,可以被继承但是无法被重写。那么在多态中,子类虽然继承到了该方法,但是从语法上已经确定无法重写了,那么编译器会默认该方法来自于父类。此时就不需要在运行期再去根据实际类型去进行寻找,而是直接到父类中进行寻找即可。那么就满足了静态绑定的条件。
2.static修饰的方法:被static修饰的方法也叫做类方法,重要的特点是与对象无关,而是直接与类型产生关联。并且在多态中如果尝试调用静态方法,那么不会发生多态的运行效果,而是会获得父类的运行结果。这是因为静态方法虽然能够被继承,并且看似满足了重写的语法,但是实际上静态方法是不能被重写的,子类只是隐藏了父类的静态方法。所以在运行时会根据引用类型直接去执行父类的方法。此时可以发现,编译器发现方法是静态方法时,可以确定是使用父类的方法,也就满足了静态绑定的条件。
3.private修饰的方法:这里不涉及多态,因为子类根本就没有访问父类私有方法的权限,所以子类也没有继承到父类的私有方法。即使子类存在声明与父类一致的该方法,也不会与父类形成重写关系。在多态环境中,尝试调用该方法时,会直接进行报错。既然私有方法无法在多态中被使用,那么就不存在动态绑定的可能,只会满足静态绑定的条件。
4.构造方法:构造方法区别于所有成员方法和静态方法,构造方法不能被手动调用,仅可以被虚拟机在创建对象的时候自动调用。并且构造方法的作用很特殊,是用于指引虚拟机如何去构建本类对象的。此时会发现,构造方法只能用于构建本类对象,所以构造方法不存在动态绑定的可能性,只会满足静态绑定的条件。
综上所述,判断是哪种绑定方式,只需要去判断是在编译期还是运行期去确定方法与类型间关联关系的即可。
问题三:这个问题在问题二中已经有详细描述。
对于同学在解决时的疑问:
对于问题一:
简单的说“Animal two = new Cat();”就可以理解为使用了多态,通常在谈及多态时只要满足“父类引用指向多态”即可。但是同学需要注意的是,真正的多态还应当有“方法调用”的步骤,只不过通常在交流的时候会省略这个部分,因为一旦使用多态,通常必然会涉及到方法的调用,所以这点就会被默认忽略。
对于问题二:
Eclipse作为代码编写工具,它的作用是帮助程序员快速进行程序的编写,所以它的提示功能也是建立在方便程序员书写代码而不是为了让程序员学习内部执行原理的。这里同学看到的提示,实际是在“运行期”会使用的方法,而不是“编译期”。同学可以尝试使用debug去了解代码真正的运行流程,同学会发现,一定会先跳转到子类,然后立即跳转到父类。这里跳转到子类的动作就是多态的体现了。
祝学习愉快~
恭喜解决一个难题,获得1积分~
来为老师/同学的回答评分吧
0 星