先上个题:
public class test {
public static void main(String[] args) {
A a = new B();
a.run();
speak(a);
}
public static void speak(A a){
System.out.println("A");
}
public static void speak(B b){
System.out.println("B");
}
static class A{
public A() {
}
public void run(){
System.out.println("A");
}
}
static class B extends A{
public B() {
}
public void run(){
System.out.println("B");
}
}
}
此时a.run()和speak(a)分别输出什么?
a.run()输出的是B
而speak(a)输出的是A
这是应为Java中的动态所导致的,但具体是怎么回事呢?在我看视频解答的时候我看到了这样一句话:编译看左,运行看右。
这样说的话可能还是太抽象了,于是我又和小伙伴们谈论了一番。
结果:
首先我们知道在JVM中的虚拟机栈中有着局部变量表存放变量的名字和类型,即:
我们能看到有一个名字叫a的变量,它的类型标记为A类型
我们再来看看字节码文件:
从第4行我们可以看出我们创建的是一个B类型的实例。
意思是,A a = new B (); 这个操作我们在局部变量表中会有一个名字为a类型为A的记录,而A类型和B类型均属于引用类型,所以栈中存放的是指向堆中对象实例的首地址。而这个首地址将会被插入到局部变量表的插槽中。
在JVM编译期间,由于Java为值传递,当传参时speak(a) 将会确定传参类型,即为A类型。
而运行时是对象调用的方法,所以是B类型的对象调用了run()方法。