我一直在玩静态方法的修饰符,并且遇到了一种奇怪的行为。
我们知道,静态方法不能被覆盖,因为它们与类而不是实例相关联。
因此,如果我有以下代码段,则编译良好 p>
//Snippet 1 - Compiles fine
public class A {
static void ts() {
}
}
class B extends A {
static void ts() {
}
}
但是如果我在A类中将final修饰符包含到静态方法中,则编译失败 B中的 ts()不能覆盖A中的ts();重写方法是静态最终。
为什么在无法覆盖静态方法时会发生这种情况?
答案 0 :(得分:152)
无法覆盖静态方法,但可以隐藏它们。 B的ts()
方法不会覆盖(不受多态性)A的ts()
,但它会隐藏它。如果您在B中呼叫ts()
(不是A.ts()
或B.ts()
只是ts()
),则会调用B中的一个而不是A.因为这不会受到影响对于多态,A中的调用ts()
永远不会被重定向到B中的调用。
关键字final
将禁止隐藏该方法。所以它们无法隐藏,尝试这样做会导致编译错误。
希望这有帮助。
答案 1 :(得分:12)
静态方法无法覆盖
这不完全正确。示例代码实际上意味着B中的方法ts隐藏了A中的方法ts。因此它不完全覆盖。在Javaranch上有一个很好的解释。
答案 2 :(得分:10)
静态方法属于类,而不属于实例。
A.ts()
和B.ts()
始终是单独的方法。
真正的问题是Java允许您在实例对象上调用静态方法。当从子类的实例调用时,具有来自父类的相同签名的静态方法是hidden。但是,您无法覆盖/隐藏final methods。
您会认为错误消息会使用隐藏字而不是覆盖...
答案 3 :(得分:5)
考虑到以下因素,您可能会发现自己可以考虑制作静态方法:
拥有以下课程:
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
。但是,根据指向对象的指针,您将从A
或B
调用方法。
现在让我们假设您是班级A
的开发人员,并且您希望允许进行子类化。但是你真的想要方法ts()
,无论什么时候被调用,甚至从子类,你做它想做的事情,而不是被子类版本隐藏。然后你可以使它final
并防止它隐藏在子类中。并且您可以确保以下代码将从您的班级A
调用该方法:
B b = new B();
b.ts();
好的,承认这是以某种方式构建的,但对某些情况可能有意义。
你不应该在实例上调用静态方法,而是直接在类上调用 - 然后你就不会遇到这个问题。例如,如果你在一个实例上调用一个静态方法,那么IntelliJ IDEA会向你显示一个警告,如果你将一个静态方法调用为最终的话。
答案 4 :(得分:1)
我认为编译错误在这里有误导性。它不应该说“被覆盖的方法是静态最终的。”,但它应该说“被覆盖的方法是最终的。”。静态修饰符在这里无关紧要。
答案 5 :(得分:0)
B中的ts()方法没有覆盖A中的ts()方法,它只是另一种方法。 B类在A中看不到ts()方法,因为它是静态的,因此它可以声明自己的方法ts()。
但是,如果方法是最终的,那么编译器会发现在A中有一个不应该在B中覆盖的ts()方法。
答案 6 :(得分:0)
与非静态方法不同,静态方法在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关键字限制了子类中具有相同名称的方法的创建。