最终静态方法的行为

我一直在使用静态方法的修饰符,遇到了一个奇怪的行为。

我们知道,静态方法不能被重写,因为它们与类而不是实例相关联。

因此,如果我有下面的代码片段,它可以很好地编译

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


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

但是如果我在类 A 中包含静态方法的 final 修饰符,那么编译就会失败 B 中的 ts ()不能重写 A 中的 ts () ; 重写的方法是 static final

为什么静态方法根本无法重写时会发生这种情况?

57264 次浏览

不能重写静态方法,但可以隐藏它们。B 的 ts()方法不会覆盖(不受多态性的影响) A 的 ts(),但是它会隐藏它。如果你在 B 中调用 ts()(不是 A.ts()B.ts()... 仅仅是 ts()) ,那么 B 中的一个将被调用,而不是 A。由于这不受多态性的影响,因此 A 中的 ts()调用永远不会被重定向到 B 中的 ts()调用。

关键字 final将禁止隐藏该方法。因此它们不能被隐藏,并且尝试这样做将导致编译器错误。

希望这个能帮上忙。

不能重写静态方法

事实并非如此。示例代码实际上意味着 B 中的方法 t 隐藏了 A 中的方法 t。所以它并不是完全凌驾于。在 贾瓦兰奇上有一个很好的解释。

B 中的 ts ()方法并没有覆盖 A 中的 ts ()方法,它只是另一个方法。B 类在 A 中看不到 ts ()方法,因为它是静态的,因此它可以声明自己的名为 ts ()的方法。

但是,如果该方法是 final 方法,那么编译器将发现 A 中有一个 ts ()方法,不应该在 B 中重写。

静态方法属于类,而不属于实例。

A.ts()B.ts()总是分开的方法。

真正的问题是 Java 允许您调用实例对象上的静态方法。具有来自父类的相同签名的静态方法在从子类的实例调用时是 藏起来了。但是,您不能覆盖/隐藏 最终方法

您可能认为错误消息会使用“隐藏”而不是“覆盖”..。

我认为这里的编译错误非常具有误导性。它不应该说“重写的方法是静态的最终。”,但它应该说“重写的方法是最终的。”.静态修饰符在这里是不相关的。

您可能会发现自己处于这样一个位置,考虑使静态方法成为最终的方法,考虑到以下情况:

开设以下课程的:

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 也会给你一个警告,如果你对一个实例调用了一个静态方法,如果你最终使用了一个静态方法。

与非静态方法不同,静态方法不能在 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 关键字限制在子类中创建具有相同名称的方法。

静态方法在继承类中是“隐藏的”,不能被“非静态”覆盖,也就是说,它们不能在“多态性”意义上被覆盖。但它们可以被“静态”覆盖。

静态 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