Java的静态绑定与动态绑定

我们可以对思考一个问题:

JVM是如何知道调用的是哪个类的方法源代码? 这里面到底有什么内幕呢?

这篇文章我们就将揭露JVM方法调用的静态(static binding) 动态绑定机制(auto binding)

理解这两个绑定之前,我们不妨先理解一下绑定一词。

何为绑定?

在Java中绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。

在Java中的绑定有分为静态绑定和动态绑定;或者说叫前期绑定或者说后期绑定。

静态绑定

程序执行前方法已经被绑定,针对Java简单的可以理解为程序编译期的绑定;

这句话是什么意思呢?也就是在编译的时候我们就知道这个方法是哪个类对应的方法。

正对Java当中的方法只有final,static,private和构造方法是前期绑定。

我们可以简单写两个Demo:

public class Father {
    public static final void f1(){
        System.out.println("Father-f1()");
    }
}
public class StaticCall {
    public static void main(String[] args) {
        Father.f1();
    }
}

我们简单的对字节码文件做个分析(这里强力推荐一个IDEA看字节码的插件jclasslib)

 我们可以看出在对应第一行中的JVM将f1()这个方法通过invokespecial放入了常量池中,然后在Father类所在的方法区中找到f1()方法的直接地址,并将这个直接地址记录到StaticCall类的常量池中的常量表中。这个过程叫常量池解析 ,以后再次调用Father.f1()时,将直接找到f1方法的字节码。

通过上面的过程,我们发现经过常量池解析之后,JVM就能够确定要调用的f1()方法具体在内存的什么位置上了。实际上,这个信息在编译阶段就已经在StaticCall类的常量池中记录了下来。这种在编译阶段就能够确定调用哪个方法的方式,我们叫做静态绑定机制 。

除了被static 修饰的静态方法,所有被private 修饰的私有方法、被final 修饰的禁止子类覆盖的方法都会被编译成invokestatic指令。另外所有类的初始化方法<init>和<clinit>会被编译成invokespecial指令。JVM会采用静态绑定机制来顺利的调用这些方法。

final:final方法虽然可以被继承,但不能被重写(覆盖),虽然子类对象可以调用,但是调用的都是父类中所定义的那个final方法,(由此我们可以知道将方法声明为final类型,一是为了防止方法被覆盖,二是为了有效地关闭java中的动态绑定)。

private:对于private的方法,首先一点它不能被继承,既然不能被继承那么就没办法通过它子类的对象来调用,而只能通过这个类自身的对象来调用。因此就可以说private方法和定义这个方法的类绑定在了一起。

动态绑定

也就是在运行时的时候对对象进线绑定。

在运行时根据具体对象的类型进行绑定。提供了一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。

其实什么时候需要用到我们的动态绑定呢?也就是说什么时候我们需要运行时来进行对象的绑定的,我们不难想出在继承的时候。需要我们做出动态绑定。

因为当我们使用一个子类的时候,子类的方法如果发生覆盖,那么此时我们方法的绑定就要做一个换绑,这个其实也是我们Java中多态的一种体现方式。

参考文章:https://blog.csdn.net/zhangjk1993/article/details/24066085

猜你喜欢

转载自blog.csdn.net/weixin_43918614/article/details/124450025