Моя проблема в том, что я не могу понять, как работает разрешение метода в следующем случае: Предположим, у нас есть два пакета A
и B
. Существует два класса, A
помещается внутри A
, B
внутри B
.
А:
package com.eka.IO.a;
import com.eka.IO.b.B;
public class A {
void foo() {
System.out.println("parent");
}
public static void main(String... args) {
B obj = new B();
obj.foo();
}
}
В:
package com.eka.IO.b;
import com.eka.IO.a.A;
public class B extends A {
public void foo() {
System.out.println("child");
}
}
Приведенный выше код печатает "child", что отлично. Но если я изменил метод main следующим образом:
public static void main(String... args) {
A obj = new B();
obj.foo();
}
код печатает "родительский", и я не понимаю, почему. (obj
имеет тип времени выполнения B
, B
имеет открытый метод foo
)
Затем я изменяю видимость foo на публикацию,
public class A {
public void foo() {
и код снова печатает "дочерний".
Насколько мне известно, методы экземпляра разрешены во время выполнения, используя следующий принцип:
- JVM проверяет класс выполнения объекта.
- JVM ищет метод класса runtime
- Если метод найден, JVM вызывает его, в противном случае переходит к родительскому классу выполнения.
В моем примере в любом из трех случаев класс выполнения для obj
всегда B
. B
метод foo
всегда открыт. Почему во втором случае JVM вызывает метод A
?
Up:
Хорошие ответы, но все же некоторые вещи неясны для меня.
a) Это компилятор, который проверяет, переопределяет ли метод другой метод. (Надеюсь, я прав).
б) в случае A obj = new B();
компилятор генерирует следующий код:
INVOKEVIRTUAL com/eka/IO/a/A.foo ()V
b1), если A foo объявлен без модификатора (видимость пакета), тогда JVM вызывает метод A. b2), если A foo объявляется общедоступным, тогда JVM вызывает метод B.
Неясно, почему во втором случае INVOKEVIRTUAL фактически называет B.foo. Как он знает, что B отменяет метод?