/**
* 方法静态解析演示
*
* @author cxt
*/
public class StaticResolution {
//静态方法
public static void say1() {
}
//final方法
public final void say2() {
}
//一般虚方法
public void say3() {
}
//重载say3
public void say3(int i) {
}
public static void main(String[] args) {
StaticResolution.say1();
StaticResolution sr = new StaticResolution();
sr.say2();
sr.say3();
sr.say3(10);
}
}
// class version 51.0 (51)
// access flags 0x21
public class StaticResolution {
// compiled from: StaticResolution.java
// access flags 0x1
public <init>() : void
L0
LINENUMBER 8 L0
ALOAD 0: this
INVOKESPECIAL Object.<init> () : void
RETURN
L1
LOCALVARIABLE this StaticResolution L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static say1() : void
L0
LINENUMBER 12 L0
RETURN
MAXSTACK = 0
MAXLOCALS = 0
// access flags 0x11
public final say2() : void
L0
LINENUMBER 17 L0
RETURN
L1
LOCALVARIABLE this StaticResolution L0 L1 0
MAXSTACK = 0
MAXLOCALS = 1
// access flags 0x1
public say3() : void
L0
LINENUMBER 22 L0
RETURN
L1
LOCALVARIABLE this StaticResolution L0 L1 0
MAXSTACK = 0
MAXLOCALS = 1
// access flags 0x1
public say3(int) : void
L0
LINENUMBER 26 L0
RETURN
L1
LOCALVARIABLE this StaticResolution L0 L1 0
LOCALVARIABLE i int L0 L1 1
MAXSTACK = 0
MAXLOCALS = 2
// access flags 0x9
public static main(String[]) : void
<strong><em style="text-decoration: underline;"> </em><span style="color:#ff6666;"><del>L0
LINENUMBER 29 L0
INVOKESTATIC StaticResolution.say1 () : void
L1
LINENUMBER 30 L1
NEW StaticResolution
DUP
INVOKESPECIAL StaticResolution.<init> () : void
ASTORE 1
L2
LINENUMBER 31 L2
ALOAD 1: sr
INVOKEVIRTUAL StaticResolution.say2 () : void
L3
LINENUMBER 32 L3
ALOAD 1: sr
INVOKEVIRTUAL StaticResolution.say3 () : void
L4
LINENUMBER 33 L4
ALOAD 1: sr
BIPUSH A // '\n' (LF)
INVOKEVIRTUAL StaticResolution.say3 (int) : void</del></span></strong>
L5
LINENUMBER 34 L5
RETURN
L6
LOCALVARIABLE args String[] L0 L6 0
LOCALVARIABLE sr StaticResolution L2 L6 1
MAXSTACK = 2
MAXLOCALS = 2
}
StaticResolution.say1 ()字样。
say2()是一个final方法,不可以重载,重写,虽然是一个invokevirtual方法但是他也是静态解析的。
say3()也是invokevirtual方法,但也是静态解析的。
say3(int i)是say3()的一个重载方法,但是重载是在编译期就确定到底要调用哪个方法,所以也是静态解析。
public class StaticDispatch {
static abstract class Human {
}
static class Man extends Human {
}
static class Woman extends Human {
}
public void sayHello(Human guy) {
System.out.println("hello,guy!");
}
public void <span style="color:#ff0000;">sayHello(Man guy)</span> {
System.out.println("hello,gentleman!");
}
public void <span style="color:#ff0000;">sayHello(Woman guy)</span> {
System.out.println("hello,lady!");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
}
}
public static main(String[]) : void
L0
LINENUMBER 32 L0
NEW StaticDispatch$Man
DUP
INVOKESPECIAL StaticDispatch$Man.<init> () : void
ASTORE 1
L1
LINENUMBER 33 L1
NEW StaticDispatch$Woman
DUP
INVOKESPECIAL StaticDispatch$Woman.<init> () : void
ASTORE 2
L2
LINENUMBER 34 L2
NEW StaticDispatch
DUP
INVOKESPECIAL StaticDispatch.<init> () : void
ASTORE 3
<span style="color:#ff0000;"> L3
LINENUMBER 35 L3
ALOAD 3: sr
ALOAD 1: man
INVOKEVIRTUAL StaticDispatch.sayHello (StaticDispatch$Human) : void
L4
LINENUMBER 36 L4
ALOAD 3: sr
ALOAD 2: woman
INVOKEVIRTUAL StaticDispatch.sayHello (StaticDispatch$Human) : void</span>
L5
LINENUMBER 37 L5
RETURN
L6
LOCALVARIABLE args String[] L0 L6 0
LOCALVARIABLE man StaticDispatch$Human L1 L6 1
LOCALVARIABLE woman StaticDispatch$Human L2 L6 2
LOCALVARIABLE sr StaticDispatch L3 L6 3
MAXSTACK = 2
MAXLOCALS = 4
对于型如Human man=new Man();其中Human我们称之为静态类型,Man我们称之为实际类型。静态类型在编译期就确定了,而实际类型要到运行时才能真正地知道是什么类型。
对于方法重载,由于方法重载是在编译器就确定到底TMD要调用哪个方法,所以重载方法的参数确定,就必须由参数的静态类型(加入参数是对象类型)来确定的。
所以TMD两个sayHello方法都会变成这样
StaticDispatch.sayHello (StaticDispatch$Human)我们注意到为静态分派,且参数的类型变为Human即静态类型。
/**
* 方法动态分派演示
* @author cxt
*/
public class DynamicDispatch {
static abstract class Human {
protected abstract void sayHello();
}
static class Man extends Human {
@Override
protected void sayHello() {
System.out.println("man say hello");
}
}
static class Woman extends Human {
@Override
protected void sayHello() {
System.out.println("woman say hello");
}
}
public static void main(String[] args) {
<span style="color:#ff0000;">Human man = new Man();
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();</span>
}
}
public static main(String[]) : void
L0
LINENUMBER 28 L0
NEW DynamicDispatch$Man
DUP
INVOKESPECIAL DynamicDispatch$Man.<init> () : void
ASTORE 1
L1
LINENUMBER 29 L1
NEW DynamicDispatch$Woman
DUP
INVOKESPECIAL DynamicDispatch$Woman.<init> () : void
ASTORE 2
<span style="color:#ff0000;"> L2
LINENUMBER 30 L2
ALOAD 1: man
INVOKEVIRTUAL DynamicDispatch$Human.sayHello () : void</span>
<span style="color:#ff0000;">L3
LINENUMBER 31 L3
ALOAD 2: woman
INVOKEVIRTUAL DynamicDispatch$Human.sayHello () : void</span>
L4
LINENUMBER 32 L4
NEW DynamicDispatch$Woman
DUP
INVOKESPECIAL DynamicDispatch$Woman.<init> () : void
ASTORE 1: man
<span style="color:#ff0000;">L5
LINENUMBER 33 L5
ALOAD 1: man
INVOKEVIRTUAL DynamicDispatch$Human.sayHello () : void</span>
L6
LINENUMBER 34 L6
RETURN
L7
LOCALVARIABLE args String[] L0 L7 0
LOCALVARIABLE man DynamicDispatch$Human L1 L7 1
LOCALVARIABLE woman DynamicDispatch$Human L2 L7 2
MAXSTACK = 2
MAXLOCALS = 3我们注意到sayHello是一个重写方法,且调用此方法的命令都是invokevirtual DynamicDispatch,也就是动态分派的,动态分派会在运行时,确定对象的实际类型,然后再确定要去调用哪个方法,这是Java最重要的一个特性。
我们看一些 invokevirtual指令的多态执行过程。
invokevirtual指令的多态查找过程开始说起,invokevirtual指令的运行时解析过程大致分为以下几个步骤:
1)找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C。
2)如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这
个方法的直接引用,查找过程结束;如果不通过,则返回java.lang.IllegalAccessError异常。
3)否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。
4)如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。
/**
* 单分派、多分派演示
* @author cxt
*/
public class Dispatch {
static class QQ {}
static class _360 {}
public static class Father {
public void hardChoice(QQ arg) {
System.out.println("father choose qq");
}
public void hardChoice(_360 arg) {
System.out.println("father choose 360");
}
}
public static class Son extends Father {
public void hardChoice(QQ arg) {
System.out.println("son choose qq");
}
public void hardChoice(_360 arg) {
System.out.println("son choose 360");
}
}
public static void main(String[] args) {
Father father = new Father();
Father son = new Son();
father.hardChoice(new _360());
son.hardChoice(new QQ());
}
}
father choose 360 son choose qq
public static main(String[]) : void
L0
LINENUMBER 34 L0
NEW Dispatch$Father
DUP
INVOKESPECIAL Dispatch$Father.<init> () : void
ASTORE 1
L1
LINENUMBER 35 L1
NEW Dispatch$Son
DUP
INVOKESPECIAL Dispatch$Son.<init> () : void
ASTORE 2
<span style="color:#ff0000;">L2
LINENUMBER 36 L2
ALOAD 1: father
NEW Dispatch$_360
DUP
INVOKESPECIAL Dispatch$_360.<init> () : void
INVOKEVIRTUAL Dispatch$Father.hardChoice (Dispatch$_360) : void</span>
<span style="color:#ff0000;"> L3
LINENUMBER 37 L3
ALOAD 2: son
NEW Dispatch$QQ
DUP
INVOKESPECIAL Dispatch$QQ.<init> () : void
INVOKEVIRTUAL Dispatch$Father.hardChoice (Dispatch$QQ) : void</span>
L4
LINENUMBER 38 L4
RETURN
L5
LOCALVARIABLE args String[] L0 L5 0
LOCALVARIABLE father Dispatch$Father L1 L5 1
LOCALVARIABLE son Dispatch$Father L2 L5 2
MAXSTACK = 3
MAXLOCALS = 3
关于JVM中方法调用的相关指令,以及解析(Resolution)和分派(Dispatch)的解释——重载的实现原理与重写的实现原理
原文地址:http://blog.csdn.net/dafeng_blog/article/details/38893891