Вызов статических методов разрешается во время компиляции (без динамической отправки).
class main {
public static void main(String args[]) {
A a = new B();
B b = new B();
a.foo();
b.foo();
a.callMe();
b.callMe();
}
}
abstract class A {
public static void foo() {
System.out.println("I am superclass");
}
public void callMe() {
foo(); //no late binding here; always calls A.foo()
}
}
class B extends A {
public static void foo() {
System.out.println("I am subclass");
}
}
дает
I am superclass
I am subclass
I am superclass
I am superclass
В Java статические поиски методов определяются во время компиляции и не могут адаптироваться к подклассам, которые загружаются после компиляции.
В двух словах статический метод переопределения не является полиморфизмом, он «скрывает метод». Когда вы переопределяете статический метод, у вас не будет доступа к методу базового класса, поскольку он будет скрыт производным классом. Использование super () приведет к ошибке времени компиляции.
Статические методы разрешаются во время компиляции. Поэтому, если вы хотите вызвать метод родительского класса, вы должны явно вызвать с именем класса или экземпляром, как показано ниже.
A.foo();
или
A a = new A();
a.foo();
Can I override a static method?
Многие слышали, что вы не можете переопределить статический метод. Это правда - вы не можете. Однако можно написать такой код:
class Foo {
public static void method() {
System.out.println("in Foo");
}
}
class Bar extends Foo {
public static void method() {
System.out.println("in Bar");
}
}
Это компилируется и выполняется просто отлично. Разве это не пример статического метода, который переопределяет другой статический метод? Ответ - нет - это пример статического метода, скрывающего другой статический метод. Если вы попытаетесь переопределить статический метод, компилятор фактически не остановит вас - он просто не делает то, что вы думаете.
Итак, какая разница?
Коротко , когда вы переопределяете метод, вы все равно получаете преимущества полиморфизма во время выполнения, а когда вы скрываете, вы этого не делаете. Так что это значит? Взгляните на этот код:
class Foo {
public static void classMethod() {
System.out.println("classMethod() in Foo");
}
public void instanceMethod() {
System.out.println("instanceMethod() in Foo");
}
}
class Bar extends Foo {
public static void classMethod() {
System.out.println("classMethod() in Bar");
}
public void instanceMethod() {
System.out.println("instanceMethod() in Bar");
}
}
class Test {
public static void main(String[] args) {
Foo f = new Bar();
f.instanceMethod();
f.classMethod();
}
}
Если вы запустите это, выход будет
instanceMethod () в Bar classMethod () в Foo
Почему мы получаем instanceMethod из Bar, но classMethod () из Foo? Разве мы не используем один и тот же экземпляр f для доступа к ним обоих?
Поскольку instanceMethod () - это (барабан, пожалуйста, ...) метод экземпляра, в котором Bar отменяет метод от Foo во время выполнения JVM использует фактический класс экземпляра f для определения того, какой метод запускать. Хотя f был объявлен как Foo, фактический экземпляр, который мы создали, был новым Bar (). Таким образом, во время выполнения JVM обнаруживает, что f является экземпляром Bar, и поэтому он вызывает instanceMethod () в Bar, а не в Foo. Так Java обычно работает, например, методами.
Однако с классомMethod (). поскольку (гм) это метод класса, компилятор и JVM не ожидают, что для вызова метода будет задействован фактический экземпляр. И даже если вы предоставите один (который мы сделали: экземпляр, упомянутый f), JVM никогда не будет смотреть на него. Компилятор будет рассматривать только объявленный тип ссылки и использовать этот объявленный тип для определения во время компиляции метода для вызова. Поскольку f объявлен как тип Foo, компилятор просматривает f.classMethod () и решает, что это означает Foo.classMethod. Не имеет значения, что экземпляр, присвоенный f, фактически является баром - для статических методов компилятор использует только объявленный тип ссылки. Это то, что мы имеем в виду, когда мы говорим, что статический метод не имеет полиморфизма во время выполнения.
Поскольку методы экземпляров и методы класса имеют это важное различие в поведении, мы используем разные термины - «переопределение», например, методов и «скрывать» для методов класса - различать эти два случая. И когда мы говорим, что вы не можете переопределить статический метод, это означает, что даже если вы пишете код, который выглядит как переопределяющий статический метод (например, первый Foo и Bar в верхней части этой страницы) - это не будет ведут себя как переопределенный метод. для более ссылки это
Просто добавьте, почему. Обычно, когда вы вызываете метод, объект, к которому принадлежит метод, используется для поиска соответствующей реализации. Вы получаете возможность переопределить метод, предоставив объекту собственный метод вместо того, чтобы использовать тот, который предоставляется родителем.
В случае статических методов нет объекта для использования, чтобы сообщить вам, какую реализацию использовать. Это означает, что компилятор может использовать только объявленный тип, чтобы выбрать реализацию метода для вызова.