是否可以覆盖派生类中的静态方法?

我有一个在基类中定义的静态方法,我想在其子类中重写此方法,是否可能?

我尝试了这个,但它没有像我预期的那样工作。 当我创建一个B类实例并调用它的callMe()方法时,会调用A类中的静态foo()方法。

public abstract class A { public static void foo() { System.out.println("I am base class"); } public void callMe() { foo(); } } Public class B { public static void foo() { System.out.println("I am child class"); } } 

静态方法调用在编译时解析(无动态调度)。

 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 

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,但我们创建的实际实例是一个新的Bar()。 因此,在运行时,JVM发现f是一个Bar实例,因此它调用Bar中的instanceMethod()而不是Foo中的实例。 这就是Java通常用于实例方法的方式。

但是使用classMethod()。 因为(ahem)它是一个类方法,所以编译器和JVM不希望需要一个实际的实例来调用该方法。 即使你提供了一个(我们做了:f引用的实例),JVM也永远不会看它。 编译器只会查看引用的声明类型,并使用该声明的类型在编译时确定要调用的方法。 由于f被声明为Foo类型,编译器会查看f.classMethod()并确定它意味着Foo.classMethod。 f引用的实例实际上是一个Bar并不重要 – 对于静态方法,编译器只使用声明的引用类型。 当我们说静态方法没有运行时多态时,这就是我们的意思。

因为实例方法和类方法在行为上有这种重要的区别,我们使用不同的术语 – 例如方法的“覆盖”和类方法的“隐藏” – 以区分这两种情况。 当我们说你不能覆盖静态方法时,这意味着即使你编写的代码看起来像是覆盖了一个静态方法(比如本页顶部的第一个Foo和Bar) – 它也不会表现得像一个重写的方法。 更多参考这个

在Java中,静态方法查找是在编译时确定的,不能适应编译后加载的子类。

只是为此添加一个原因。 通常,当您调用方法时,该方法所属的对象用于查找适当的实现。 您可以通过让对象提供自己的方法而不是使用父级提供的方法来覆盖方法。

在静态方法的情况下,没有用于告诉您使用哪个实现的对象。 这意味着编译器只能使用声明的类型来选择要调用的方法的实现。

不,这是不可能的。

这里和这里有一些相似(不一样)的问题。

静态方法在编译时解析。因此,如果要调用父类方法,则应使用className或instance显式调用,如下所示。

 A.foo(); 

要么

  A a = new A(); a.foo(); 

简而言之,静态方法覆盖不是多态,它是“方法隐藏”。 当您重写静态方法时,您将无法访问基类方法,因为它将被派生类隐藏。使用super()将引发编译时错误。