最终静态方法的行为
我一直在用静态方法使用修饰符,并遇到了一个奇怪的行为。
我们知道,静态方法不能被覆盖,因为它们与类而不是实例相关联。
所以,如果我有下面的代码片段,它编译好
//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
类的。 但取决于指向该对象的指针,您将从A
或B
调用方法。
现在让我们假设你是类A
的开发者,你想允许子类。 但是,无论什么时候调用,甚至是从一个子类,你都真的想要方法ts()
,这是做你想做的事情,而不是一个被覆盖的版本。 然后你可以使它成为final
并防止它被“覆盖”/被重新定义在子类中。 你可以确定下面的代码会调用你的类A
的方法:
B b = new B(); b.ts();
好吧,承认这是某种构造,但对某些情况可能有意义。
你不应该在实例上调用静态方法,而是直接在类上调用 – 那么你就不会有这个问题。 另外IntelliJ IDEA例如将显示一个警告,如果你调用一个实例的静态方法,以及如果你最后一个静态方法。
B中的ts()方法不是覆盖A中的ts()方法,它只是另一种方法。 B类没有看到A中的ts()方法,因为它是静态的,所以它可以声明自己的方法,称为ts()。
但是,如果方法是final的,那么编译器将会得到A中有一个ts()方法,它不应该在B中被覆盖。