Java — 静态绑定和动态绑定

7-李建涛
7-李建涛   编辑于 2018-11-05 15:50
阅读量: 112

 

       

       Java的引用变量有两个类型,一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,会出现所谓的多态。(即Person p=new Teacher();编译时引用变量p类型为Person,运行时p引用变量类型为Teacher,这种情况叫多态)

        因为子类其实是一种特殊的父类,因此java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换,或者被称为向上转型,由系统自动完成。(编译时)

     (Java在运行时创建对象是先new一个子类大小的空间(这个空间根据子类相对父类扩展部分所需大小和父类创建空间所需大小的和来指定,但都属于子类的空间),然后调用父类构造方法对这块空间的父类部分进行初始化,然后调用子类构造方法对其余部分进行初始化(实际上是在调用子类构造方法时通过super(Xxx)调用了父类构造方法))

 

        引用变量在编译阶段只能调用其编译时类型所具有的方法(就是说整块空间父类部分所具有的方法),但运行时则执行它运行时类型所具有的方法(整块空间的方法),因此,编写Java代码时,引用变量只能调用声明该变量所用类里包含的方法。与方法不同的是,对象的属性则不具备多态性。通过引用变量来访问其包含的实例属性时,系统总是试图访问它编译时类所定义的属性,而不是它运行时所定义的属性。(即父类中的属性继承到子类,又在子类扩展部分定义了相同的属性,在空间中有两个相同的属性,只是在不同的区域,运行时取属性取的是空间中父类部分的属性,但getXxx()方法取用的是子类扩展部分中的属性)

以上提到的没有final、static、private三个关键字,即都属于动态绑定,下面介绍什么是绑定和静态绑定的规则。

       绑定:一个方法的调用与方法所在的类关联起来。java中的绑定分为静态绑定和动态绑定,又被称作前期绑定和后期绑定。

  静态绑定:(final、static、private)在程序执行前已经被绑定,也就是说在编译过程中就已经知道这个方法是哪个类的方法,此时由编译器获取其他连接程序实现。

  动态绑定:在运行根据具体对象的类型进行绑定。

注意:Java中除了static和final方法(private方法属于final方法,因为类中的private方法被隐式指定为final方法,由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)之外,其他方法都是后期绑定。这意味着通常不必判定是否该进行后期绑定,因为它是自动发生的。

1.静态绑定(成员属性和编译时的方法)

  private:能被继承,访问权限 不能通过子类对象调用,而只能通过类本身的对象进行调用,所以可以说private方法和方法所属的类绑定;

  final:final方法虽然可以被继承,但是不能被重写(覆盖),虽然子类对象可以调用,但是调用的都是父类继承过来的final方法(因此可以看出当类中的方法声明为final的时候,一是为了防止方法被覆盖,而是为了有效关闭java的动态绑定);

  static:static方法可以被子类继承,但是不能被子类重写(覆盖),但是可以被子类隐藏。(这里意思是说子类引用指向子类对象时,如果父类里有一个static方法,它的子类里如果没有对应的方法,那么当子类对象调用这个方法时就会使用父类中的方法。而如果子类中定义了相同的方法,则会调用子类的中定义的方法。(以上声明类型和对象类型一致都为子类)唯一的不同就是,当子类对象上转型为父类对象时(声明强制转换为父类引用),不论子类中有没有定义这个静态方法,该对象都会使用父类中的静态方法。因此这里说静态方法可以被隐藏而不能被覆盖。隐藏和覆盖的区别在于,子类对象转换成父类对象后,能够访问父类被隐藏的变量和方法,而不能访问父类被覆盖的方法)。(方法的隐藏只在static中)

2.动态绑定(运行时的方法)

  调用的方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定。动态绑定的过程分为以下几个环节:

  (1)编译器查看对象的声明类型和方法名;

  (2)编译器查看调用方法时提供的参数类型。例如x.f("hello"),编译器将会挑选f(String),而不是f(int),由于存在类型转换(int转换为double),所以可能会更复杂。如果编译器没找到参数类型匹配的方法,或者发现有多个方法与之匹配,就会报告一个错误。

  至此,编译器获得了需要调用的方法名字和参数类型。

  (3)采用动态绑定调用方法的时候,一定调用与所引用对象的实际类型最合适的类的方法。如果x的实际类型是D,它是C类的子类,如果D定义了一个方法f(String),就直接调用它,否则将在D类的超类中寻找f(String)(属于子类从父类继承的部分),以此类推。(没有三个关键字的情况,因为是运行时动态绑定,引用的声明类型自动转换成跟对象一致了,当父类和子类都有相同的方法且父类方法没有static修饰符时,即同一块空间父类部分和子类扩展部分有相同的方法且父类部分的方法没有static修饰符时,调用子类的方法,子类扩展部分没有相同的方法,就调用父类部分的方法)

 

(相同的方法:方法名 参数 返回值相同,修饰符可以不同)

 

重写与重载容易区分,直接看方法就可以。

 

(重写(覆盖)和隐藏的区别:

你在子类中写了一个和父类相同的方法(参数相同 返回值条件存在),如果父类的方法有static,则为隐藏,创建实例时还会访问父类的方法,如果没有static,则为重写,创建实例时不能访问到父类的方法。)

 

部分引用自https://blog.csdn.net/zhangjk1993/article/details/24066085,我把我写的放在了括号中,如有错误,欢迎指正。

收藏 1 转发 评论 1

方法是存在方法区中的,为了方便理解就直接写在对象里了