最终静态方法的行为

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

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

所以,如果我有下面的代码片段,它编译好

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

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

为什么当静态方法根本无法被覆盖时发生这种情况?

静态方法不能被覆盖,但可以隐藏。 B的ts()方法不是覆盖(不受多态性)的A的ts() ,但它会隐藏它。 如果你在B(不是A.ts()B.ts() …就是ts() )中调用ts() ,那么B中的一个将被调用,而不是A.因为这不是多态,所以在A中调用ts()将永远不会被redirect到B中的那个。

final关键字将禁用隐藏的方法。 所以他们不能隐藏,试图这样做会导致编译器错误。

希望这可以帮助。

静态方法不能被重写

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

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

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类的对象。 但是,由于您是从typesA的引用调用它,它将调用A.ts() 。 您可以使用以下代码打印B

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

在这两种情况下,您拥有的对象实际上都是来自B类的。 但取决于指向该对象的指针,您将从AB调用方法。

现在让我们假设你是类A的开发者,你想允许子类。 但是,无论什么时候调用,甚至是从一个子类,你都真的想要方法ts() ,这是做你想做的事情,而不是一个被覆盖的版本。 然后你可以使它成为final并防止它被“覆盖”/被重新定义在子类中。 你可以确定下面的代码会调用你的类A的方法:

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

好吧,承认这是某种构造,但对某些情况可能有意义。

你不应该在实例上调用静态方法,而是直接在类上调用 – 那么你就不会有这个问题。 另外IntelliJ IDEA例如将显示一个警告,如果你调用一个实例的静态方法,以及如果你最后一个静态方法。

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

但是,如果方法是final的,那么编译器将会得到A中有一个ts()方法,它不应该在B中被覆盖。