隐藏和覆盖的区别和用法

隐藏和覆盖的区别和用法

以下内容来自隐藏和覆盖的区别和用法

静态类型和动态类型

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

比如对于两个类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中类的属性也都是静态绑定的。这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态。

隐藏和覆盖

当子类继承父类时,除了继承父类所有的成员变量和成员方法之外,还可以声明自己的成员变量和成员方法。那么,如果父类和子类的成员变量和方法同名会发生什么?假设有一个父类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();
    }

}
//声明父类
class Father{
    static int a=0;
    int b=0;
    void c() {
        System.out.println(0);
    }
    static void d() {
        System.out.println(0);
    }
}
//声明子类
class Son extends Father{
    static int a=1;
    int b=1;
    void c() {
        System.out.println(1);
    }
    static void d() {
        System.out.println(1);
    }
}

运行结果为:

0

0

1

0


相关讨论(来源于geeklub官方qq群)

题目:

为什么string a = NULL;string b = NULL; c=a+b; 得出来c的结果为NULL?

回答:

与字符串有关的加法会自动创建一个 StringBuilder对象,然后所有的都直接 append进去。如果传进去的对象是 null 会调用一个 appendNULL,这个方法是把 n u l l四个字符append进去,所以任何类的null对象加进去都是一个null字符串。

这个从JVM6.0开始就是用的这样的处理方式了 ,隐式调用toString 然后null的话返回null是JVM6.0之前的做法 那个时候toString还不是Object里的方法 算是Object的一个隐藏超类的方法,那个隐藏超类允许为null然后 toString返回null 但是后面全部合并到了Object里


补充:面向对象的三个特点

封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的

什么是多态

指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式

实现多态的技术

动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法

多态存在的三个必要条件

  1. 继承
  2. 重写
  3. 父类引用指向子类对象(超类又称为父类)

上一篇
Ruby的基础知识 Ruby的基础知识
RubyInterpreted vs CompiledDepending on the programming language you’re using, it will either be a compiled language or
2018-11-28
下一篇
c的指针 c的指针
指针的基本概念,一维数组的指针。 什么是指针指针也称作指针变量,大小为4个字节(或8个字节)的变量,其内容代表一个内存地址。 sizeof(T*) 4字节(64位计算机上可能是8字节) T是任何变量类型 指针的定义 类型名 *
2018-11-22
目录