如果派生类不能重写/更改静态方法的定义,为什么以及何时需要在派生类中重新声明静态方法?

Emm*_*mmy 0 java static

我对static关键字及其用法很熟悉。我知道static可以在子类中重新声明方法,但是方法的定义被隐藏,并且与父类相同。我提到的是我已经阅读的文章的一些链接:

https://www.geeksforgeeks.org/can-we-overload-or-override-static-methods-in-java/

Java为什么不允许覆盖静态方法?

静态和最终之间的区别?

当派生类定义的静态方法与基类中的静态方法具有相同的签名时,派生类中的方法会将方法隐藏在基类中。但是仍然将基类的方法称为display()基类的方法。

但是我很好奇为什么为什么以及何时需要重新声明派生类中的static 方法,base class如果它的定义不能在派生类中被覆盖/更改,而是显示基类的定义呢?

    /* Java program to show that if static method is redefined by 
a derived class, then it is not overriding. */

// Superclass 
class Base { 

    // Static method in base class which will be hidden in subclass 
    public static void display() { 
        System.out.println("Static or class method from Base"); 
    } 

    // Non-static method which will be overridden in derived class 
    public void print() { 
        System.out.println("Non-static or Instance method from Base"); 
    } 
} 

// Subclass 
class Derived extends Base { 

    // This method hides display() in Base 
    public static void display() { 
        System.out.println("Static or class method from Derived"); 
    } 

    // This method overrides print() in Base 
    public void print() { 
        System.out.println("Non-static or Instance method from Derived"); 
} 
} 

// Driver class 
public class Test { 
    public static void main(String args[ ]) { 
    Base obj1 = new Derived(); 

    // As per overriding rules this should call to class Derive's static 
    // overridden method. Since static method can not be overridden, it 
    // calls Base's display() 
    obj1.display(); 

    // Here overriding works and Derive's print() is called 
    obj1.print();    
    } 
} 
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 6

这里的主要内容是:不要使用实例变量来调用static函数。Java允许它,但是它令人困惑,因为它看起来像是实例方法调用,而实际上却并非如此。

此行创建一个Derived实例,并将其分配给Base-typed变量:

Base obj1 = new Derived(); 
Run Code Online (Sandbox Code Playgroud)

稍后,当您键入:

obj1.display();
Run Code Online (Sandbox Code Playgroud)

真正发生的是:

Base.display();
Run Code Online (Sandbox Code Playgroud)

因为obj是类型变量,Base并且displaystatic方法。实际上,如果您查看编译后的字节码,就会发现它确实是调用,实例变量在任何地方都没有提及:

公开课测试{
  公共Test();
    码:
       0:加载_0
       1:invokespecial#1 //方法java / lang / Object。“” :()V
       4:返回

  公共静态void main(java.lang.String []);
    码:
       0:新#2 //派生类
       3:dup
       4:invokespecial#3 //方法派生。“” :()V
       7:astore_1
       8:aload_1
       9:流行
      10:invokestatic#4 //方法Base.display :()V
      13:aload_1
      14:invokevirtual#5 //方法Base.print :()V
      17:返回
}

注意obj1.display()通话之间的区别

      10:invokestatic#4 //方法Base.display :()V

obj1.print()致电:

      13:aload_1
      14:invokevirtual#5 //方法Base.print :()V

static调用不会压入obj1堆栈,而是使用invokestatic。实例调用确实压入obj1堆栈(aload_1),并调用invokevirtual

如果声明obj1为type Derived,则会看到相反的行为。带有:

Derived obj1 = new Derived(); 
Run Code Online (Sandbox Code Playgroud)

然后

obj1.display();
Run Code Online (Sandbox Code Playgroud)

输出:

派生的静态或类方法

因为它确实是:

Derived.display();
Run Code Online (Sandbox Code Playgroud)

全部与实例变量的类型有关。实例本身的类型根本不重要(实际上obj1可能是null)。

不要static通过实例变量调用方法。而是使用类名称。

我知道static可以在子类中重新声明方法,但是方法的定义被隐藏,并且与父类相同。

...

当派生类定义的static方法与基类中的静态方法具有相同的签名时,派生类中的方法会将方法隐藏在基类中。

这两个陈述相互矛盾。关系是这样的:当通过派生类进行引用时,派生static方法将覆盖基础static方法。因此,使用您的代码,Base.display引用Base.displayDerived.displayreferences Derived.display。有一个事实displayDerived不会影响到一个上Base,这仍然是通过访问Base(或Base-typed变量)。(如果您没有displayin DerivedDerived.display将引用Base.display,但既然您这样做了,则不会。)

但是我很好奇为什么以及何时需要在派生类中重新声明基类的静态方法...

您唯一需要的时间就是您是否希望Derived.display(或obj1.display何时obj1被声明为Derived)做与Base.display(或obj1.display何时obj1被声明为Base)不同的事情。我可以想到的一个示例是可能通过Builder模式创建实例的方法:

Base b = Base.builder()
         .setName("the name")
         .setAnswer(42)
         .setBiz("baz")
         .build();
Run Code Online (Sandbox Code Playgroud)

Derived d = Derived.builder()
         .setName("the name")
         .setAnswer(42)
         .setBiz("baz")
         .setDerivedThingy("whatever")
         .build();
Run Code Online (Sandbox Code Playgroud)