Основной вывод здесь: не используйте экземпляр переменные для вызова static
функций.Java допускает это, но это сбивает с толку, потому что похоже на вызов метода экземпляра, когда это не так.
Эта строка создает экземпляр Derived
и присваивает его переменной Base
:
Base obj1 = new Derived();
Позже, когда вы наберете:
obj1.display();
, на самом деле произойдет:
Base.display();
, потому что obj
- это переменная типа Base
и display
- это метод static
.На самом деле, если вы посмотрите на скомпилированный байт-код, вы увидите, что это буквально этот вызов, переменная экземпляра нигде не упоминается:
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class Derived
3: dup
4: invokespecial #3 // Method Derived."":()V
7: astore_1
8: aload_1
9: pop
10: invokestatic #4 // Method Base.display:()V
13: aload_1
14: invokevirtual #5 // Method Base.print:()V
17: return
}
Обратите внимание на разницу между obj1.display()
вызов
10: invokestatic #4 // Method Base.display:()V
и obj1.print()
вызов:
13: aload_1
14: invokevirtual #5 // Method Base.print:()V
static
вызов не проталкивает obj1
в стек, ииспользует invokestatic
.Вызов экземпляра делает push obj1
в стек (aload_1
) и вызывает invokevirtual
.
Если вы объявили obj1
типа Derived
, вы быувидеть противоположное поведение.С:
Derived obj1 = new Derived();
затем
obj1.display();
выходами:
Static or class method from Derived
, потому что это действительно:
Derived.display();
Это все о тип переменной экземпляра.Тип самого экземпляра вообще не имеет значения (на самом деле obj1
может быть null
).
Не вызывать static
методы через переменные экземпляра.Вместо этого используйте имя класса.
Я понимаю, что метод static
может быть повторно объявлен в подклассе, но его определение скрыто и остается таким же, как и у родительского класса.
...
Когда производный класс определяет метод static
с такой же сигнатурой, что и у статического метода в базовом классе, метод в производном классе скрывает метод в базовом классе.
Эти два утверждения противоречат друг другу.Отношение таково: производный метод static
переопределяет базовый метод static
при обращении через производный класс.Итак, с вашим кодом Base.display
ссылки Base.display
и Derived.display
ссылки Derived.display
.Тот факт, что display
на Derived
не влияет на тот на Base
, который все еще доступен через Base
(или переменную типа Base
).(Если бы у вас не было display
в Derived
, Derived.display
будет ссылаться на Base.display
, но, поскольку у вас есть, это не так.)
Но мне любопытнопочему и когда необходимо повторно объявить статический метод базового класса в производном классе ...
Единственное время, которое вам нужно, это если вы хотите Derived.display
(или obj1.display
, когдаobj1
объявлен как Derived
) для выполнения действий, отличных от Base.display
(или obj1.display
, когда obj1
объявлен как Base
).Один пример, который я могу вспомнить, это методы, которые создают экземпляры, возможно, с помощью шаблона Builder:
Base b = Base.builder()
.setName("the name")
.setAnswer(42)
.setBiz("baz")
.build();
vs.
Derived d = Derived.builder()
.setName("the name")
.setAnswer(42)
.setBiz("baz")
.setDerivedThingy("whatever")
.build();