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 () в баре
classMethod () в Foo
Почему мы получаем instanceMethod из Bar, а classMethod () из Foo? Разве мы не используем один и тот же экземпляр f для доступа к ним обоим? Да, мы - но так как один переопределяет, а другой скрывается, мы видим другое поведение.
Поскольку instanceMethod () является (барабанная дробь, пожалуйста ...) методом экземпляра, в котором Bar переопределяет метод из Foo, во время выполнения JVM использует фактический класс экземпляра f, чтобы определить, какой метод запустить. Хотя f был объявлен как Foo, фактический экземпляр, который мы создали, был новым Bar (). Поэтому во время выполнения JVM обнаруживает, что f является экземпляром Bar, и поэтому она вызывает instanceMethod () в Bar, а не в Foo. Вот как обычно работает Java для методов экземпляра.
С помощью classMethod (). поскольку (хм) это метод класса, компилятору и JVM не требуется фактический экземпляр для вызова метода. И даже если вы предоставите один (который мы сделали: экземпляр, на который ссылается f), JVM никогда не будет смотреть на него. Компилятор будет смотреть только на объявленный тип ссылки и использовать этот объявленный тип, чтобы определить во время компиляции, какой метод вызывать. Поскольку f объявлен как тип Foo, компилятор смотрит на f.classMethod () и решает, что это означает Foo.classMethod. Неважно, что экземпляр, на который ссылается f, на самом деле является Bar - для статических методов компилятор использует только объявленный тип ссылки. Вот что мы имеем в виду, когда говорим, что статический метод не имеет полиморфизма во время выполнения.
Поскольку методы экземпляра и методы класса имеют это важное различие в поведении, мы используем разные термины - «переопределение» для методов экземпляра и «скрытие» для методов класса - чтобы различать два случая. И когда мы говорим, что вы не можете переопределить статический метод, это означает, что даже если вы напишите код, который выглядит так, как будто он переопределяет статический метод (например, первые Foo и Bar вверху этой страницы) - это не будет вести себя как переопределенный метод. для более подробной информации это