Java中隐藏和覆盖的区别和用法

讲隐藏和覆盖之前先看两个概念:静态类型动态类型

任何一个引用变量都有两个类型:一个叫静态类型,也就是定义该引用变量的类型;另一个叫动态类型,也就是该引用实际指向的对象类型。

比如对于两个类A和类B,有:A a=new B();

那么,引用a的静态类型就是A,动态类型就是B。

Java中引用的静态类型在编译的时候就可以确认,但是编译器无法得知这个引用的动态类型;只有当程序运行时,通过RTTI就可以检查出引用的动态类型。

再介绍一下,Java中绑定的概念:对于一个程序,可以有很多的方法。这些方法的名称、参数类型和参数数量都可能相同或者不同,那么在调用一个方法的时候,如何将一个方法和该方法所在的类关联起来,这就是绑定。java中的绑定分为静态绑定和动态绑定。

静态绑定:所有依赖于静态类型来将某方法和该方法所在的类关联起来的动作都是静态绑定。因为静态绑定在程序运行前发生,所有又叫前期绑定。

动态绑定:所有依赖于动态类型来将某方法和该方法所在的类关联起来的动作都是动态绑定。因为动态绑定是在程序运行时,通过RTTI实现,所以又叫后期绑定。

举例:假如有一个父类Father和一个子类Son,子类重写了父类中的某个方法method()。有以下语句:

Father father=new Son();

father.method();

对于这个例子,静态绑定的过程是:java文件编译时,编译器检查出引用father的静态类型是Father类,由于将method()方法和父类Father关联起来。也就是说,程序运行前编译器是无法检查出引用father的动态类型的,所以会直接调用静态类型中对应的方法。

而动态绑定的过程是:当这个java程序运行起来了,RTTI检查出引用father的动态类型是Son类时,会将method()方法和子类Son关联起来,也就是决定调用动态类型Son类中的method()方法。具体过程为:①JVM提取对象的实际类型的方法表;②JVM搜索方法签名;③调用方法。

另外,要补充的是:java中类的属性也都是静态绑定的。这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态。

--------------------------------------------分割线--------------------------------------------

下面来说一下,java中的隐藏和覆盖的概念。我们知道,当子类继承父类时,除了继承父类所有的成员变量和成员方法之外,还可以声明自己的成员变量和成员方法。那么,如果父类和子类的成员变量和方法同名会发生什么?假设有一个父类Father和一个子类Son。父类有一个成员变量a=0;有一个静态成员变量b=0;有一个成员方法a,输出0;有一个静态成员方法b,输出0。子类分别重写这些变量和方法,只是修改变量的值和方法的输出,全部改为1.  我们再声明一个静态类型是父类,动态类型是子类的引用:

Father father=new Son();

通过这个引用访问子类的变量和调用子类的方法,那么,会有以下结论:

1、所有的成员变量(不管是静态还是非静态)都只进行静态绑定,所以JVM的绑定结果会是:直接访问静态类型中的成员变量,也就是父类的成员变量,输出0.

2、对于静态方法,也是只进行静态绑定,所以JVM会通过引用的静态类型,也就是Father类,来进行绑定,结果为:直接调用父类中的静态方法,输出0.

3、对于非静态方法,会进行动态绑定,JVM检查出引用father的动态类型,也就是Son类,绑定结果为:调用子类中的静态方法,输出1.

对于1和2这两种情况,子类继承父类后,父类的属性和静态方法并没有被子类抹去,通过相应的引用可以访问的到。但是在子类中不能显示地看到,这种情况就称为隐藏。

而对于3这种情况,子类继承父类后,父类的非静态方法被子类重写后覆盖上去,通过相应的引用也访问不到了(除非创建父类的对象来调用)。这种情况称为覆盖。

总结一下,就是,子类继承父类后:

父类的成员变量只会被隐藏,而且支持交叉隐藏(比如静态变量被非静态变量隐藏)。父类的静态方法只会被静态方法隐藏,不支持交叉隐藏。父类的非静态方法会被覆盖,但是不能交叉覆盖。

代码测试如下:

package test;
/* 隐藏和覆盖的区别 */
public class HideAndCover {
    public static void main(String[] args) {
        Father father=new Son();
        System.out.println(father.a);
        System.out.println(father.b);
        father.c();
        father.d();
    }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/4e551e3b85752c4de3c07c5aec54c3fd.html