【问题标题】:Is it possible to override a static method in derived class?是否可以覆盖派生类中的静态方法?
【发布时间】:2018-12-17 11:23:28
【问题描述】:

我在基类中定义了一个静态方法,我想在它的子类中重写这个方法,可以吗?

我试过了,但它没有按我的预期工作。当我创建 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");
  }
}

【问题讨论】:

  • 你不应该首先从一个对象调用静态方法,你应该从类中调用它。

标签: java static


【解决方案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();
    }
}

如果你运行这个,输出是

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

【讨论】:

    【解决方案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
    

    【讨论】:

    • 我改变主意并写了“无动态调度”,因为这是涉及的一般概念。
    • 正确的术语是“超类”和“子类”,而不是“基类”和“子类”。
    • 我只是复制粘贴了 MartinDenny2069 的问题。我更改了帖子以反映您的建议。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        没有。这是不可能的。

        一些类似(不一样)的问题here 和here。

        【讨论】:

          【解决方案6】:

          静态方法在编译时解析。所以如果你想调用父类方法,那么你应该显式调用 className 或实例,如下所示。

          A.foo();
          

          或

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              猜你喜欢
              • 2016-03-17
              • 2011-09-11
              • 2011-06-01
              • 2020-12-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多