2009-11-16 75 views
94

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

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

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

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

class B extends A { 
    static void ts() { 
    } 
} 

但是,如果我包括final修饰符在类的静态方法,然后编译失败 TS()在B不能覆盖TS()在一个;重写的方法是static final

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

+0

它似乎很奇怪,这个问题的+1,但直到现在,没有一个答案是令人满意的。 – 2009-11-16 17:54:59

+1

它没有被覆盖。它仍然在A.ts()。 – 2009-11-16 18:03:09

回答

131

静态方法不能被覆盖,但可以隐藏。 B的ts()方法不是重写(不受多态性)的A的ts(),但它会隐藏它。如果您在B中拨打ts()(NOT A.ts()B.ts() ...只是ts()),则B中的一个将被调用,而不是A.由于这不会受到多态性影响,因此A中的调用ts()将永远不会重定向到B.

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

希望这会有所帮助。

+25

为了也许完成你的答案,我相信这是正确的,这里的问题基本上是一个糟糕的编译器错误信息:它应该说B不能*隐藏A中的ts()。声明静态方法final是声明它不能被隐藏。 – 2009-11-16 17:58:28

+2

@肖恩欧文:我也这么想。术语'隐藏'甚至在Java规范中使用,所以为什么不在编译器消息中使用它。 – NawaMan 2009-11-16 18:10:45

+2

为什么这甚至是一个功能?在什么情况下它会有用? – immibis 2015-03-25 04:32:48

11

静态方法不能被覆写

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

+3

这是真的只是不精确。静态方法不能被重载,但如果你在实例引用而不是类名称上调用它们,则可以隐藏它们。 – 2015-02-26 15:09:04

+0

不幸的是,您的链接不再工作。有没有可能解决这个问题? – 2017-09-12 11:54:43

0

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

然而,如果该方法是最终,则编译器将拿起有在A中的TS()方法,它不应该在B.覆盖

+0

我不认为这解释了为什么'最终'突然意味着这些方法不能共存。正如你所说,没有'最后',没有问题。你说这不是重写,但是接下来说问题是B不能重写A的方法。 – 2009-11-16 17:55:11

+0

确实如此,我声明B在A中看不到方法ts()(它是'隐藏'),但最终修饰符不会从扩展另一个类的类'隐藏'方法。但是,呃,好的。 – amischiefr 2009-11-16 18:03:49

9

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

A.ts()B.ts()总是要分开的方法。

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

你可能会认为该错误信息将使用隐藏,而不是覆盖了字...

1

我觉得编译错误是相当误导这里。它不应该说“重写的方法是静态最终的”,但它应该反而说“重写的方法是最终的”。静态修饰符在这里是无关紧要的。

+0

所以你认为B中的静态方法重写A中的那个? – 2017-06-24 10:07:11

4

你可能会发现自己在考虑制作一个静态方法,最后,考虑到以下位置:

具有以下类:

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类。但取决于指向该对象的指针,您将调用方法AB

现在我们假设您是A类的开发人员,并且您希望允许子分类。但是,无论何时调用,即使是从子类中调用,您都确实想要方法ts(),这是做你想做的事情,而不是被子类版本隐藏。那么你可以使它成为final,并防止它被隐藏在子类中。你可以肯定的是,下面的代码将调用该方法从A类:

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

好吧,admittetly以某种方式建造,但它可能是有意义的一些情况。

你不应该在实例上调用静态方法,而是直接调用类 - 那么你就不会有这个问题。例如,如果你在一个实例上调用一个静态方法,以及如果你最终创建一个静态方法,那么IntelliJ IDEA会向你显示警告。