1、当子类和父类存在同一个方法,子类重写了父类的方法,程序在运行时调用方法是调用父类的方法还是子类的重写方法呢?
2、当一个类中存在方法名相同但参数不同(重载)的方法,程序在执行的时候该如何辨别区分使用哪个方法呢?
在Java中我们使用静态绑定(static binding)和动态绑定(Dynamic binding)来解决,那么什么是绑定?什么是静态绑定?什么又是动态绑定?有什么区别?
绑定、静态绑定、动态绑定的概念
-
绑定
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。
对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。 -
静态绑定
在程序执行前方法已经被绑定,针对java简单的可以理解为程序编译期的绑定;
java当中的方法只有final,static,private和构造方法是前期绑定 -
动态绑定
在运行时根据具体对象的类型进行绑定。提供了一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。
动态绑定的过程:
虚拟机提取对象的实际类型的方法表;
虚拟机搜索方法签名;
调用方法。
静态绑定 VS 动态绑定
1、静态绑定是发生在编译阶段;而动态绑定是在运行阶段;
2、private, final and static方法和变量使用静态绑定,而虚函数(virtual methods)则会根据运行时的具体对象进行绑定(注:在Java语言中, 所有的方法默认都是”虚函数”。只有以关键字 final 标记的方法才是非虚函数。)
3、静态绑定使用的是类信息,而动态绑定使用的是对象信息
4、重载方法(overloaded methods)使用的是静态绑定,而重写方法(overridden methods)使用的是动态绑定
下面从代码中分析
静态绑定
public class StaticBindingTest {
public static void main(String args[]) {
StaticBindingTest sbt = new StaticBindingTest();
sbt.test("This is a String");
sbt.test(10);
}
public void test(String s) {
System.out.println(s);
}
public void test(int a) {
System.out.println(a);
}
}
输出结果
This is a String
10
从上面的结果可以得出:
在两个同名但参数不同的方法(重载),在调用方法sbt.test(param)时,程序会自动根据输入的参数类型来选择具体调用哪个方法,其后的原理就是静态绑定,即在编译期根据参数类型进行静态绑定。
java当中的向上转型或者说多态是借助于动态绑定实现的,所以理解了动态绑定,也就搞定了向上转型和多态。
动态绑定的典型发生在父类和子类的转换声明之下:
比如:Parent p = new Child();
其具体过程细节如下:
1:编译器检查对象的声明类型和方法名。假设我们调用p.method()方法,并且p已经被声明为Child类的对象,那么编译器会列举出Child类中所有的名称为method的方法和从Child类的父类继承过来的method方法
2:接下来编译器检查方法调用中提供的参数类型。如果在所有名称为method 的方法中有一个参数类型和调用提供的参数类型最为匹配,那么就调用这个方法,这个过程叫做“重载解析”
3:当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同p所指向的对象的实际类型相匹配的方法版本。假设child类定义了mehod()那么该方法被调用,否则就在child的父类(Parent类)中搜寻方法method()
动态绑定
子类有父类的重写方法
//父类
public class Parent {
protected String name = "ParentName";
public void method() {
System.out.println("ParentMethod");
}
}
//子类
public class Child extends Parent {
protected String name = "ChildName";
public void method() {
System.out.println("ChildMethod");
}
public static void main(String[] args) {
Parent p = new Child();
System.out.println(p.name);
p.method();
}
}
输出结果
ParentName
ChildMethod
子类中没有父类的重写方法
//测试类
public class DynamicBindingTest {
public static void main(String[] args) {
Parent p = new Child();
System.out.println(p.name);
p.method();
}
}
//父类
class Parent {
protected String name = "ParentName";
public void method() {
System.out.println("ParentMethod");
}
}
//子类
class Child extends Parent {
protected String name = "ChildName";
}
输出结果:
ParentName
ParentMethod
从上面的结果中可以看出:
1、子类的对象(由父类的引用handle)调用到的是父类的成员变量,运行时(动态)绑定针对的范畴只是对象的方法,而属性要采取静态绑定方法。
2、执行p.method()时会先去调用子类的method方法执行,若子类没有则向上转型去父类中寻找。
所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性还是父类的属性。