方法重载与invokevirtual字节码指令的关系

这次来看一下跟方法调用相关的东东,对于字节码而言跟方法调用的助记符有如下几个:

  • invokeinterface:调用接口中的方法,实际上是在运行期决定的,决定到底调用实现该接口的哪个对象的方法。
  • invokestatic:调用静态方法。【重要】
  • invokespectial:调用自己的私有方法、构造方法(<int>)以及父类的方法。
  • invokevirtual:调用虚方法,在c++中是存在虚方法这个概念的,但是Java是不存在的,但是在字节码中是存在有虚方法的,运行期动态查找的过程。【重要】
  • invokedynamic:动态调用方法。这是五个调用中最为复杂的,但是它是在JDK1.7之后才引用的,本身Java是一门静态的语言,但是通过一些引擎可以调用Javascript,这里了解一下既可。

下面来用代码先来了解一下invokstatic助记符:

接下来编译,然后用jclasslib查看一下字节码信息:

其实这种静态方法的调用是属于静态解析,关于静态解析总共有4个情形:

  • 静态方法
  • 父类方法
  • 构造方法
  • 私有方法:【为啥不能是公有方法呢?因为公有方法是能够被父类重写的,既然存在重写就会存在多态的可能,而私有方法是不可能被子类重写的】

以上4类方法称为非虚方法,他们是类加载阶段就可以将符号引用转换为直接引用的。

接下来再来看另外一个程序,会得到另外一个理论化的东东:

那程序的输出结果是什么呢?不卖关子了,直接看结果,其结果肯定会颠覆我们的眼睛:

这逆天的结果是为啥呢?其实这里涉及到“方法的静态分派”的理论化的东东,下面来看一下这块的理论:

对于这句代码而言:

g1的静态类型是Grandpa【特别注意这个,既始我们强制类型转换也不会改变g1的静态类型,比如:(Father)g1】,而g1的实际类型(真正指向的类型)是Father,而变量的静态类型是不会发生变化的,而变量的实际类型是可以发生变化的(多态的一种体现),实际类型是在运行期方可确定的。而我们定义的三个test()方法其实是属于方法的重载,如下:

重点来了:对于方法重载,其实它是一种静态的行为,既然是静态的行为,那么调用方法传的参数则会只认变量的静态类型,而非变量的实际类型,如下:

这就是原因之所在,下面来查看一下它的字节码信息:

关于Java的方法重载和重写最大的区别就在于:方法重载它是一个静态形为【也就是像咱们这个例子,在编译其就已经确定了】,而方法重写则是一个动态形为【在编译期确定不了,得到运行期才行】。

posted on 2018-09-29 13:41  cexo  阅读(820)  评论(2编辑  收藏  举报

导航