由于 Java 的编译没有C C++ 编译过程中的链接阶段,所以 Class 文件中储存的只是符号引用,等到了在运行时才通过符号引用定位到方法区中方法代码在内存布局中的位置--直接引用。
符号引用到直接引用的替换又涉及两种方式。一种是解析,另一种是分派。解析发生在类加载的解析阶段,分派发生在编译或方法调用阶段。
public class StaticDispatch {
static abstract class Human { }
static class Man extends Human { }
static class Woman extends Human { }
static class Child extends Human { }
public void say(Human human) {
System.out.println("human");
}
public void say(Man man) {
System.out.println("man");
}
public void say(Woman woman) {
System.out.println("woman");
}
public void say(Child child) {
System.out.println("child");
}
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
Human child = new Child();
StaticDispatch dispatch = new StaticDispatch();
dispatch.say(man);
dispatch.say(woman);
dispatch.say(child);
}
main 方法的执行结果:
human
human
human
虽然 StaticDispatch 为每种 Human 的子类都重载了一个 say 方法,但是由于重载采用的是静态分派,是根据对象的静态类型做方法匹配的。所以结果全都匹配到了 public void say(Human human) 方法。main 方法编译之后的字节码:
public static main([Ljava/lang/String;)V
NEW method_invoke/StaticDispatch$Man
DUP
INVOKESPECIAL method_invoke/StaticDispatch$Man.<init> ()V
ASTORE 1
NEW method_invoke/StaticDispatch$Woman
DUP
INVOKESPECIAL method_invoke/StaticDispatch$Woman.<init> ()V
ASTORE 2
NEW method_invoke/StaticDispatch$Child
DUP
INVOKESPECIAL method_invoke/StaticDispatch$Child.<init> ()V
ASTORE 3
NEW method_invoke/StaticDispatch
DUP
INVOKESPECIAL method_invoke/StaticDispatch.<init> ()V
ASTORE 4
// 下面为调用 say
ALOAD 4
ALOAD 1
INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V
ALOAD 4
ALOAD 2
INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V
ALOAD 4
ALOAD 3
INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V
RETURN
public class DynamicDispatch {
static class Human {
public void say() {
System.out.println("human");
}
}
static class Man extends Human {
@Override
public void say() {
System.out.println("man");
}
}
static class Woman extends Human {
@Override
public void say() {
System.out.println("woman");
}
}
}
public static void main(String[] args) {
Human human = new Human();
Human man = new Man();
Human woman = new Woman();
human.say();
man.say();
woman.say();
}
main 方法的执行结果:
human
man
woman
意料之中,所谓的多态就是这样。那多态是如何实现的?
其实多态的实现过程也就是确定被重写的方法版本的过程。main 方法编译之后的字节码:
public static main([Ljava/lang/String;)V
NEW method_invoke/DynamicDispatch$Human
DUP
INVOKESPECIAL method_invoke/DynamicDispatch$Human.<init> ()V
ASTORE 1
NEW method_invoke/DynamicDispatch$Man
DUP
INVOKESPECIAL method_invoke/DynamicDispatch$Man.<init> ()V
ASTORE 2
NEW method_invoke/DynamicDispatch$Woman
DUP
INVOKESPECIAL method_invoke/DynamicDispatch$Woman.<init> ()V
ASTORE 3
// 下面为多态调用 say
ALOAD 1
INVOKEVIRTUAL method_invoke/DynamicDispatch$Human.say ()V
ALOAD 2
INVOKEVIRTUAL method_invoke/DynamicDispatch$Human.say ()V
ALOAD 3
INVOKEVIRTUAL method_invoke/DynamicDispatch$Human.say ()V
RETURN
由于 Java 的编译没有C C++ 编译过程中的链接阶段,所以 Class 文件中储存的只是符号引用,等到了在运行时才通过符号引用定位到方法区中方法代码在内存布局中的位置--直接引用。
符号引用到直接引用的替换又涉及两种方式。一种是解析,另一种是分派。解析发生在类加载的解析阶段,分派发生在编译或方法调用阶段。
public class StaticDispatch {
static abstract class Human { }
static class Man extends Human { }
static class Woman extends Human { }
static class Child extends Human { }
public void say(Human human) {
System.out.println("human");
}
public void say(Man man) {
System.out.println("man");
}
public void say(Woman woman) {
System.out.println("woman");
}
public void say(Child child) {
System.out.println("child");
}
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
Human child = new Child();
StaticDispatch dispatch = new StaticDispatch();
dispatch.say(man);
dispatch.say(woman);
dispatch.say(child);
}
main 方法的执行结果:
human
human
human
虽然 StaticDispatch 为每种 Human 的子类都重载了一个 say 方法,但是由于重载采用的是静态分派,是根据对象的静态类型做方法匹配的。所以结果全都匹配到了 public void say(Human human) 方法。main 方法编译之后的字节码:
public static main([Ljava/lang/String;)V
NEW method_invoke/StaticDispatch$Man
DUP
INVOKESPECIAL method_invoke/StaticDispatch$Man.<init> ()V
ASTORE 1
NEW method_invoke/StaticDispatch$Woman
DUP
INVOKESPECIAL method_invoke/StaticDispatch$Woman.<init> ()V
ASTORE 2
NEW method_invoke/StaticDispatch$Child
DUP
INVOKESPECIAL method_invoke/StaticDispatch$Child.<init> ()V
ASTORE 3
NEW method_invoke/StaticDispatch
DUP
INVOKESPECIAL method_invoke/StaticDispatch.<init> ()V
ASTORE 4
// 下面为调用 say
ALOAD 4
ALOAD 1
INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V
ALOAD 4
ALOAD 2
INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V
ALOAD 4
ALOAD 3
INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V
RETURN
public class DynamicDispatch {
static class Human {
public void say() {
System.out.println("human");
}
}
static class Man extends Human {
@Override
public void say() {
System.out.println("man");
}
}
static class Woman extends Human {
@Override
public void say() {
System.out.println("woman");
}
}
}
public static void main(String[] args) {
Human human = new Human();
Human man = new Man();
Human woman = new Woman();
human.say();
man.say();
woman.say();
}
main 方法的执行结果:
human
man
woman
意料之中,所谓的多态就是这样。那多态是如何实现的?
其实多态的实现过程也就是确定被重写的方法版本的过程。main 方法编译之后的字节码:
public static main([Ljava/lang/String;)V
NEW method_invoke/DynamicDispatch$Human
DUP
INVOKESPECIAL method_invoke/DynamicDispatch$Human.<init> ()V
ASTORE 1
NEW method_invoke/DynamicDispatch$Man
DUP
INVOKESPECIAL method_invoke/DynamicDispatch$Man.<init> ()V
ASTORE 2
NEW method_invoke/DynamicDispatch$Woman
DUP
INVOKESPECIAL method_invoke/DynamicDispatch$Woman.<init> ()V
ASTORE 3
// 下面为多态调用 say
ALOAD 1
INVOKEVIRTUAL method_invoke/DynamicDispatch$Human.say ()V
ALOAD 2
INVOKEVIRTUAL method_invoke/DynamicDispatch$Human.say ()V
ALOAD 3
INVOKEVIRTUAL method_invoke/DynamicDispatch$Human.say ()V
RETURN