一个Java程序的执行要经过编译和执行(解释)这两个步骤,同时Java又是面向对象的编程语言。当子类和父类存在同一个方法,子类重写了父类的方法,程序在运行时调用方法是调用父类的方法还是子类的重写方法呢,这应该是我们在初学Java时遇到的问题。这里首先我们将确定这种调用何种方法实现或者变量的操作叫做绑定。
在Java中存在两种绑定方式,一种为静态绑定,又称作早期绑定。另一种就是动态绑定,亦称为后期绑定。
区别
静态绑定发生在编译时期,动态绑定发生在运行时
使用private或static或final修饰的变量或者方法,使用静态绑定。而虚方法(可以被子类重写的方法)则会根据运行时的对象进行动态绑定。
静态绑定使用类信息来完成,而动态绑定则需要使用对象信息来完成。
重载(Overload)的方法使用静态绑定完成,而重写(Override)的方法则使用动态绑定完成。
静态绑定
private:private修饰的方法属于私有方法,不会被子类调用或者复写,只有这个类本身对象可以使用,因此是静态绑定的。
final: final修饰的方法可以被继承,但不能被重写和修改,因此子类虽然有这个方法,单调用的还是父类的方法,因此是静态绑定
static:static修饰的方法是静态方法,属于类方法,直接通过类就可以访问,不依赖于具体对象,因此也是静态绑定
构造方法: 构造方法是不能被继承的,因此编译也是知道这个构造方法属于哪个类,因此也是静态绑定
public class TestMain {
public static void main(String[] args) {
String str = new String();
Caller caller = new Caller();
caller.call(str);
}
static class Caller {
public void call(Object obj) {
System.out.println("an Object instance in Caller");
}
public void call(String str) {
System.out.println("a String instance in in Caller");
}
}
}
这个是方法的重写,采用的就是静态绑定的方式
动态绑定
动态绑定是指在运行阶段,通过对象实例、方法名称、参数类型、参数个数等信息动态判断要调用哪个对象的哪个方法。
动态绑定的典型发生在父类和子类的转换声明之下
具体过程细节如下:
编译器检查对象的声明类型和方法名
接下来编译器检查方法调用中提供的参数类型。
程序运行并且使用动态绑定调用方法时,虚拟机必须调用所指向的对象的实际类型相匹配的方法版本。
public class TestMain {
public static void main(String[] args) {
String str = new String();
Caller caller = new SubCaller();
caller.call(str);
}
static class Caller {
public void call(String str) {
System.out.println("a String instance in Caller");
}
}
static class SubCaller extends Caller {
@Override
public void call(String str) {
System.out.println("a String instance in SubCaller");
}
}
}
上面是重写的实例,可以看到子类重写了父类的方法。当我们调用的时候调用的是子类的方法,而不是父类的方法,这个就是动态绑定。
如果大家不能确定,可以通过反编译上面的代码,就可以看到是不是动态绑定了
注意
动态绑定还是静态绑定只是针对方法来说的,对于成员变量来说,一般意义上都是静态绑定的,比如下面这段代码
public class Father {
protected String name = "father";
}
public class Son extends Father {
protected String name = "son";
public void say(){
System.out.println(name);
}
public static void main(String[] args) {
Father son = new Son();
System.out.println(son.name);
}
}
结果:
father
可见通过父类的引用拿到的是父类的属性,所以要说明的是动态绑定只针对方法
那么要拿到子类的属性怎么办,可以通过getter方法
public class Father {
protected String name = "父亲属性";
public String getName() {
return name;
}
}
public class Son extends Father {
protected String name = "儿子属性";
public String getName() {
return name;
}
public static void main(String[] args) {
Father sample = new Son();
System.out.println("调用的属性:" + sample.getName());
}
}
总结
静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态,多态是java的一大特色。多态也是面向对象的关键技术之一,所以java是以效率为代价来实现多态这是很值得的。