java 最终静态方法的行为

xzv2uavs  于 2022-12-25  发布在  Java
关注(0)|答案(8)|浏览(166)

我一直在尝试使用静态方法的修饰符,并遇到了一个奇怪的行为。
正如我们所知,静态方法不能被重写,因为它们与类而不是示例相关联。
所以如果我有下面的代码片段,它可以很好地编译

//Snippet 1 - Compiles fine
public class A {
    static void ts() {
    }
}

class B extends A {
    static void ts() {
    }
}

但是如果我在类A的静态方法中包含final修饰符,那么编译就会失败B中的ts()不能覆盖A中的ts();重写的方法是静态final
当静态方法根本不能被覆盖时,为什么会发生这种情况?

mcdcgff0

mcdcgff01#

静态方法不能被重写,但可以隐藏。B的ts()方法未被重写(不受多态性影响)的ts(),但它会隐藏它。如果在B中调用ts()(不是A.ts()B.ts() ...只是ts()),则将调用B中的一个,而不是A。由于这不受多态性的影响,A中的呼叫ts()将永远不会被重定向到B中的呼叫。
关键字final将禁止隐藏该方法。因此,它们不能被隐藏,试图隐藏它们将导致编译器错误。

hc2pp10m

hc2pp10m2#

不能重写静态方法
这并不完全正确。示例代码实际上意味着B中的方法ts隐藏了A中的方法ts。所以它并不完全覆盖。在Javaranch上有一个很好的解释。

cotxawn7

cotxawn73#

静态方法属于类,而不是示例。
A.ts()B.ts()总是独立的方法。
真实的的问题是Java允许你在一个示例对象上调用静态方法。当从子类的示例调用时,具有来自父类的相同签名的静态方法是hidden。但是,你不能覆盖/隐藏final methods
你可能会认为错误消息会使用隐藏这个词而不是覆盖...

rjee0c15

rjee0c154#

您可能会发现自己处于考虑将静态方法设置为final的位置,请考虑以下几点:
具有以下类别:

class A {
    static void ts() {
        System.out.print("A");
    }
}
class B extends A {
    static void ts() {
        System.out.print("B");
    }
}

现在调用这些方法的“正确”方法应该是

A.ts();
B.ts();

这将导致AB,但您也可以在示例上调用这些方法:

A a = new A();
a.ts();
B b = new B();
b.ts();

这也将导致AB
现在考虑以下内容:

A a = new B();
a.ts();

这将打印A,这可能会让你感到惊讶,因为你实际上有一个B类的对象,但是因为你是从A类型的引用调用它,它将调用A.ts(),你可以用下面的代码打印B

A a = new B();
((B)a).ts();

在这两种情况下,你的对象实际上都来自B类,但是根据指向对象的指针,你将从AB调用方法。
现在假设你是类A的开发者,你想允许子类化,但是你真的希望方法ts()无论何时被调用,即使是从子类调用,那就是做你想让它做的事情,而不是被子类版本隐藏。然后你可以让它成为final,并防止它在子类中被隐藏。并且可以确保以下代码将调用类A中的方法:

B b = new B();
b.ts();

好吧,诚然,这是某种构造,但它可能是有意义的一些情况。
你不应该在示例上调用静态方法,而应该直接在类上调用--这样你就不会有这个问题了。同样,IntelliJ IDEA也会向你显示一个警告,如果你在示例上调用静态方法,以及如果你使静态方法成为final。

jmo0nnb3

jmo0nnb35#

B中的ts()方法没有覆盖A中的ts()方法,它只是另一个方法。B类看不到A中的ts()方法,因为它是静态的,因此它可以声明自己的方法ts()。
但是,如果方法是final的,那么编译器会发现A中有一个ts()方法不应该在B中被覆盖。

7ivaypg9

7ivaypg96#

我认为编译错误在这里是相当误导的。它不应该说“overrided method is static final ",而应该说“overrided method is final"。static修饰符在这里是无关紧要的。

d4so4syb

d4so4syb7#

在Java中,静态方法不像非静态方法那样可以被覆盖。但是,它们可以像静态和非静态数据成员一样被继承。这就是为什么不能在父类中创建同名的非静态方法

class Writer { 
    public static void doo(){
        System.out.println("sth");
    } 
}
class Author extends Writer{ 
    public void doo(){
        System.out.println("ok"); // error overridden method is static
    }
}

final关键字确保每次调用方法时都运行特定的方法体。现在,如果在子类中创建了一个同名的静态方法,并且调用了该方法,子类中的方法被执行,如果在父类中的静态方法名之前加上final前缀,则不应该是这种情况。因此final关键字限制了在子类中创建同名的方法。

ghhaqwfi

ghhaqwfi8#

静态方法在继承类中是"隐藏的",不能被"非静态地"覆盖,也就是说,它们不能在"多态性"意义上被覆盖,但是它们可以被"静态地"覆盖。
静态final方法不允许被重写,即使是"静态地"。
下面的例子说明了这一点-
App.java-

public class App {
    public static void main(String[] args) {
        Base.fun1();
        Base.fun2();
        Derived1.fun1();
        Derived1.fun2();
        Derived2.fun1();
        Derived2.fun2();
    }
}

Base.java-

public abstract class Base {

    protected static void fun1() {
        System.out.println("Static fun1() called in Base");
    }

    protected static final void fun2() {
        System.out.println("Static final fun2() called in Base");
    }

}

Derived1.java-

public class Derived1 extends Base {

    protected static void fun1() {
        System.out.println("Static fun1() called in Derived1");
    }
    
    //Not allowed to override final method
    /*
    protected static final void fun2() {
        System.out.println("Static final fun2() called in Derived1");
    }
    */
}

Derived2.java-

public class Derived2 extends Base {

    protected static void fun1() {
        System.out.println("Static fun1() called in Derived2");
    }
    
    //Not allowed to override final method
    /*
    protected static final void fun2() {
        System.out.println("Static final fun2() called in Derived2");
    }
    */
}

输出-

Static fun1() called in Base
Static final fun2() called in Base
Static fun1() called in Derived1
Static final fun2() called in Base
Static fun1() called in Derived2
Static final fun2() called in Base

相关问题