是否可以重写派生类中的静态方法?

2022-09-04 23:48:00

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

我试过这个,但它没有像我预期的那样工作。当我创建类 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");
  }
}

答案 1

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),它的行为也不会像一个被覆盖的方法。欲了解更多信息,请参考


答案 2

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

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

推荐