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() in Bar
classMethod() in Foo
为什么我们从 Bar 获得 instanceMethod,而从 Foo 获得 classMethod()?我们不是使用相同的实例 f 来访问这两个实例吗?是的,我们是 - 但是由于一个是压倒性的,另一个是隐藏的,我们看到不同的行为。
由于 instanceMethod() 是一个实例方法,其中 Bar 覆盖了 Foo 中的方法,因此在运行时,JVM 使用实例 f 的实际类来确定要运行的方法。尽管 f 被声明为 Foo,但我们创建的实际实例是一个新的 Bar()。因此,在运行时,JVM 发现 f 是 Bar 实例,因此它在 Bar 中调用 instanceMethod() 而不是 Foo 中的实例。这就是Java通常如何为实例方法工作。
不过,使用classMethod()。由于(哎呀)它是一个类方法,编译器和JVM不希望需要一个实际的实例来调用该方法。即使你提供了一个(我们做了:f引用的实例),JVM也永远不会看到它。编译器将仅查看引用的已声明类型,并使用该声明类型在编译时确定要调用的方法。由于 f 被声明为 Foo 类型,编译器会查看 f.classMethod() 并确定它表示 Foo.classMethod。f 引用的实例实际上是 Bar 并不重要 - 对于静态方法,编译器仅使用引用的声明类型。这就是我们说静态方法没有运行时多态性的意思。
由于实例方法和类方法在行为上具有这种重要的差异,因此我们使用不同的术语 ( “重写” 表示实例方法,“隐藏” 表示类方法 ) 来区分这两种情况。当我们说你不能重写静态方法时,这意味着即使你编写的代码看起来像是重写了静态方法(如本页顶部的第一个Foo和Bar),它的行为也不会像一个被覆盖的方法。欲了解更多信息,请参考此