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();
}
}
如果你运行这个,输出是
bar中的instanceMethod()
Foo 中的 classMethod()
为什么我们从 Bar 获取 instanceMethod,而从 Foo 获取 classMethod()?我们不是使用同一个实例 f 来访问这两个吗?是的,我们是——但由于一个是压倒一切的,另一个是隐藏的,我们会看到不同的行为。
由于 instanceMethod() 是(请打鼓...)一个实例方法,其中 Bar 覆盖了 Foo 中的方法,因此在运行时 JVM 使用实例 f 的实际类来确定运行哪个方法。尽管 f 被声明为 Foo,但我们创建的实际实例是 new Bar()。所以在运行时,JVM 发现 f 是一个 Bar 实例,所以它在 Bar 中调用 instanceMethod() 而不是 Foo 中的那个。这就是 Java 通常用于实例方法的方式。
虽然使用 classMethod()。因为(咳咳)它是一个类方法,编译器和 JVM 并不期望需要一个实际的实例来调用该方法。即使您提供了一个(我们提供了:f 引用的实例),JVM 也永远不会查看它。编译器只会查看引用的声明类型,并使用该声明类型在编译时确定要调用哪个方法。由于 f 被声明为 Foo 类型,编译器查看 f.classMethod() 并确定它表示 Foo.classMethod。 f 引用的实例实际上是 Bar 无关紧要 - 对于静态方法,编译器仅使用引用的声明类型。这就是我们所说的静态方法不具有运行时多态性的意思。
因为实例方法和类方法在行为上存在这一重要差异,所以我们使用不同的术语——实例方法的“覆盖”和类方法的“隐藏”——来区分这两种情况。当我们说您不能覆盖静态方法时,这意味着即使您编写的代码看起来像是覆盖了静态方法(如本页顶部的第一个 Foo 和 Bar) - 它也不会表现得像一个被覆盖的方法。更多请参考this