我一直在尝试使用静态方法的修饰符,并遇到了一个奇怪的行为。
正如我们所知,静态方法不能被重写,因为它们与类而不是示例相关联。
所以如果我有下面的代码片段,它可以很好地编译
//Snippet 1 - Compiles fine
public class A {
static void ts() {
}
}
class B extends A {
static void ts() {
}
}
但是如果我在类A的静态方法中包含final修饰符,那么编译就会失败B中的ts()不能覆盖A中的ts();重写的方法是静态final。
当静态方法根本不能被覆盖时,为什么会发生这种情况?
8条答案
按热度按时间mcdcgff01#
静态方法不能被重写,但可以隐藏。B的
ts()
方法未被重写(不受多态性影响)的ts()
,但它会隐藏它。如果在B中调用ts()
(不是A.ts()
或B.ts()
...只是ts()
),则将调用B中的一个,而不是A。由于这不受多态性的影响,A中的呼叫ts()
将永远不会被重定向到B中的呼叫。关键字
final
将禁止隐藏该方法。因此,它们不能被隐藏,试图隐藏它们将导致编译器错误。hc2pp10m2#
不能重写静态方法
这并不完全正确。示例代码实际上意味着B中的方法ts隐藏了A中的方法ts。所以它并不完全覆盖。在Javaranch上有一个很好的解释。
cotxawn73#
静态方法属于类,而不是示例。
A.ts()
和B.ts()
总是独立的方法。真实的的问题是Java允许你在一个示例对象上调用静态方法。当从子类的示例调用时,具有来自父类的相同签名的静态方法是hidden。但是,你不能覆盖/隐藏final methods。
你可能会认为错误消息会使用隐藏这个词而不是覆盖...
rjee0c154#
您可能会发现自己处于考虑将静态方法设置为final的位置,请考虑以下几点:
具有以下类别:
现在调用这些方法的“正确”方法应该是
这将导致
AB
,但您也可以在示例上调用这些方法:这也将导致
AB
。现在考虑以下内容:
这将打印
A
,这可能会让你感到惊讶,因为你实际上有一个B
类的对象,但是因为你是从A
类型的引用调用它,它将调用A.ts()
,你可以用下面的代码打印B
:在这两种情况下,你的对象实际上都来自
B
类,但是根据指向对象的指针,你将从A
或B
调用方法。现在假设你是类
A
的开发者,你想允许子类化,但是你真的希望方法ts()
无论何时被调用,即使是从子类调用,那就是做你想让它做的事情,而不是被子类版本隐藏。然后你可以让它成为final
,并防止它在子类中被隐藏。并且可以确保以下代码将调用类A
中的方法:好吧,诚然,这是某种构造,但它可能是有意义的一些情况。
你不应该在示例上调用静态方法,而应该直接在类上调用--这样你就不会有这个问题了。同样,IntelliJ IDEA也会向你显示一个警告,如果你在示例上调用静态方法,以及如果你使静态方法成为final。
jmo0nnb35#
B中的ts()方法没有覆盖A中的ts()方法,它只是另一个方法。B类看不到A中的ts()方法,因为它是静态的,因此它可以声明自己的方法ts()。
但是,如果方法是final的,那么编译器会发现A中有一个ts()方法不应该在B中被覆盖。
7ivaypg96#
我认为编译错误在这里是相当误导的。它不应该说“overrided method is static final ",而应该说“overrided method is final"。static修饰符在这里是无关紧要的。
d4so4syb7#
在Java中,静态方法不像非静态方法那样可以被覆盖。但是,它们可以像静态和非静态数据成员一样被继承。这就是为什么不能在父类中创建同名的非静态方法
final
关键字确保每次调用方法时都运行特定的方法体。现在,如果在子类中创建了一个同名的静态方法,并且调用了该方法,子类中的方法被执行,如果在父类中的静态方法名之前加上final前缀,则不应该是这种情况。因此final关键字限制了在子类中创建同名的方法。ghhaqwfi8#
静态方法在继承类中是"隐藏的",不能被"非静态地"覆盖,也就是说,它们不能在"多态性"意义上被覆盖,但是它们可以被"静态地"覆盖。
静态final方法不允许被重写,即使是"静态地"。
下面的例子说明了这一点-
App.java
-Base.java
-Derived1.java
-Derived2.java
-输出-