Static详解和多态的深入理解
static详解
任何一个引用变量都有两个类型
- 静态类型:定义该引用变量的类型
- 动态类型:该引用变量实际指向的对象类型
例如
A a = new B();
//引用变量a的静态类型就是A,动态类型就是B
Java中引用的静态类型在编译时就可以确认,但是编译器无法得知这个引用的动态类型,只有当程序运行时,通过RTTI就可以检查出引用的动态类型。
绑定
绑定的概念:对于一个程序,可以有很多的方法,这些方法的名称,参数类型和参数数量都可能相同或者不同,那么调用一个方法的时候,如何将一个方法和该方法所在的类关联起来就是绑定。
Java中的绑定分为静态绑定和动态绑定。
静态绑定
静态绑定:所有依赖于静态类型来将某方法和该方法所在的类关联起来的行为都是静态绑定。因为静态绑定是在编译时,程序运行前发生,也叫前期绑定。
动态绑定
动态绑定:所有依赖于动态类型来将某方法和该方法所在的类关联起来的行为都是动态绑定。因为动态绑定是在运行时,通过RTTI实现,也叫后期绑定。
例如有一个父类和子类,子类重写了父类中的某个方法method()
Father father = new Son();
father.method();
对于这个例子
-
静态绑定的过程为:Java文件编译时,编译器检查出引用father的静态类型是Father类,由于将method()方法和父类的方法Father关联起来。也就是说,程序编译时编译器是无法检查出引用father的动态类型的,所以会直接调用静态类型中对应的方法。
-
动态绑定的过程为:Java文件运行时,RTTI检查出引用father的动态类型是Son类,会将method()方法和子类Son关联起来,也就是说调用动态类型Son类中的method()方法。
具体过程为
- JVM提取对象的实际类型的方法表
- JVM搜索方法签名
- 调用方法
Java中类的属性也都是静态绑定的,这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样可以提高程序的运行效率!对方法采取动态绑定是为了实现多态。
多态的隐藏和覆盖
当子类继承父类时,除了继承父类所有的成员变量和成员方法之外,还可以声明自己的成员变量和成员方法。
那么如果父类和子类的成员变量和方法同名会发生什么?
public class Father{//父类属性和方法
a=0;
static b=0;
public void a(){
sout("0");
}
public static void b(){
sout("0")
}
}
public class Son extends Father{//子类属性和方法
a=1;
static b=1;
public void a(){
sout("1");
}
public static void b(){
sout("1")
}
}
然后声明一个静态类型是父类,动态类型是子类的引用
Father father = new Son();
通过这个引用访问子类的变量和调用子类的方法
得出以下结论
- 所有的成员变量(无论是静态还是动态)都只进行静态绑定,所以JVM的绑定结果会是:直接访问静态类型中的成员变量,也就是父类的成员变量,输出0.
- 对于静态方法,也是只进行静态绑定,所以JVM会通过引用的静态类型,也就是Father类,来进行绑定,结果为:直接调用父类中的静态方法,输出0.
- 对于非静态方法,会进行动态绑定,JVM检查出引用father的动态类型,也就是Son类,绑定结果为:调用子类中的非静态方法,输出1.
对于1和2这两种情况,子类继承父类后,父类的属性和静态方法并没有被子类抹去,通过相应的引用可以访问的到。但是在子类中不能显式的看到,这种情况称为隐藏。
对于3这种情况,子类继承父类后,父类的非静态方法被子类重写后覆盖上去,通过相应的引用也访问不到了(除非创建父类的对象来调用)。这种情况称为覆盖。
总结:子类继承父类之后
父类的成员变量只会被隐藏,而且支持交叉隐藏(静态变量被费静态变量隐藏。父类的静态方法只会被静态方法隐藏,不支持交叉隐藏。父类的非静态方法会被覆盖,但是不能交叉覆盖。

浙公网安备 33010602011771号