最终静态方法的行为


123

我一直在使用静态方法修改器,并且遇到了奇怪的行为。

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

因此,如果我具有以下代码段,则可以正常编译

//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

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


2
似乎很奇怪,这个问题+1,但是到目前为止,没有一个答案令人满意。
Rakesh Juyal 09年

1
它没有被覆盖。它仍然在A.ts()处。
Alex Feinman

Answers:


166

静态方法不能被覆盖,但是它们可以被隐藏。ts()B 的方法不会覆盖(不会受到多态性的影响)ts()A的方法,但是会隐藏它。如果您ts()以B 调用(NOT A.ts()B.ts()... just ts()),则将调用B的一个,而不调用A。由于它没有进行多态处理,因此ts()A中的调用将永远不会重定向到B中的一个。

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

希望这可以帮助。


30
为了完成您的答案,这是正确的,我相信,这里的问题基本上是一个错误的编译器错误消息:它应该说B不能在A中隐藏 ts()。声明一个static方法final是声明它不能被隐藏。
肖恩·欧文

2
@Sean Owen:我也是。Java规范中甚至使用了“隐藏”一词,因此为什么不在编译器消息中使用它。
NawaMan

2
为什么这甚至是功能?在什么情况下有用?
user253751

公共类Test {最终静态public void main(String ... srik){System.out.println(“ In main method”); ts(); } public static void ts(){Child c = new Child(); c.ts(); System.out.println(“ Test ts”); }}公共类Child扩展Test {public static void ts(){System.out.println(“ Child ts”); }}你好,你能解释一下在这种情况下会发生什么吗
srikanth r

@SeanOwen我也不认为这是正确的,编译器应该说,由于A#ts正在被继承并且这样的方法已经存在于中B,仅具有两个具有相同签名和不同修饰符(final)的方法就不能作为重载。不过,我希望我能想到一个简单的消息
尤金(Eugene)

13

静态方法不能被覆盖

这不是真的。该示例代码实际上意味着B中的ts方法将A中的ts方法隐藏了。因此,它并不是完全覆盖。在Javaranch上有一个很好的解释。


4
确实是不精确的。如果要在实例引用而不是类名上调用静态方法,则不能覆盖静态方法,但可以将其隐藏。
John Mercier

1
不幸的是,您的链接不再起作用。有可能解决这个问题吗?
Mathias Bader

javaranch链接无法正常工作,但是在代码牧场上使用Google搜索关键字来打开此链接
Sundeep

我通过用Sundeep发布的链接替换无效链接来编辑帖子。
MC皇帝

10

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

A.ts()并且B.ts()将始终是单独的方法。

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

您会认为错误消息将使用“隐藏”一词,而不是覆盖...


6

考虑以下因素,您可能会发现自己可以考虑将静态方法定为最终方法:

具有以下课程:

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。因为您实际上有一个class对象,这可能会让您感到惊讶B。但是由于您是从type的引用中调用它的A,所以它将调用A.ts()。您可以B使用以下代码进行打印:

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

在这两种情况下,您拥有的对象实际上都是来自class的B。但是,根据指向对象的指针,您将从A或从调用方法B

现在,假设您是类的开发人员,A并且想要允许子类化。但是,您真的想要method ts(),无论何时从子类中调用它,这都是您想要的,并且不会被子类版本隐藏。然后,您可以制作它final并防止它被隐藏在子类中。并且您可以确保以下代码将从您的类中调用该方法A

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

好的,诚然,这是经过某种方式构造的,但在某些情况下可能有意义。

您不应在实例上调用静态方法,而应在类上直接调用-这样就不会有问题。另外,例如,如果您在实例上调用静态方法以及将静态方法定为最终方法,则IntelliJ IDEA也会向您显示警告。


0

B中的ts()方法不会覆盖A中的ts()方法,它只是另一种方法。由于B类是静态的,因此它在A中看不到ts()方法,因此它可以声明自己的方法ts()。

但是,如果该方法是最终方法,则编译器将发现A中存在ts()方法,该方法不应在B中覆盖。


我不认为这解释了为什么“最终”突然意味着这些方法无法共存。如您所说,没有“最终”,就没有问题。您说它没有覆盖,但是然后说问题是B无法覆盖A的方法。
肖恩·欧文

当然可以,我声明B在A中看不到ts()方法(它是“隐藏的”),但是final修饰符没有“隐藏”来自扩展另一个方法的类的方法。但是,嗯,好的。
amischiefr

0

我认为编译错误在这里极具误导性。它不应该说“覆盖的方法是static final。”,而应该说“覆盖的方法是final”。静态修饰符在这里无关紧要。


1
因此,您认为B中的静态方法会覆盖A中的静态方法吗?
Koray Tugay

@KorayTugay我只是想知道编译器是否首先查看潜在可重写的方法(暂时忽略静态),在失败时看到final。虽然只是一个疯狂的猜测
尤金(Eugene)

Balus我认为这是您在StackOverflow中唯一的低质量答案。考虑到您所有的特殊答案,这个答案不属于他们。@BalusC
Koray Tugay,

@KorayTugay:当时我没有足够的声誉来发表评论:)如果您回信,我会删除答案,没问题。
BalusC

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关键字限制子类中具有相同名称的方法的创建。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.