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

Mar*_*069 11 java static

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

我尝试了这个,但它没有像我预期的那样工作.当我创建类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");
  }
}
Run Code Online (Sandbox Code Playgroud)

Rup*_*dav 27

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这编译并运行得很好.这不是一个覆盖另一个静态方法的静态方法的例子吗?答案是否定的 - 这是隐藏另一个静态方法的静态方法的一个例子.如果您尝试覆盖静态方法,编译器实际上并不会阻止您 - 它只是不按您的想法执行操作.

那有什么区别?

简而言之,当您覆盖方法时,您仍然可以获得运行时多态性的好处,而当您隐藏时,则不会.那是什么意思呢?看看这段代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你运行它,输出是


Foo 中Bar classMethod()中的instanceMethod()

为什么我们从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) - 它也不会表现得像一个重写的方法.更多参考这个


Art*_*cto 18

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

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

I am superclass
I am subclass
I am superclass
I am superclass
Run Code Online (Sandbox Code Playgroud)