2010-09-09 63 views
7

示例1:为什么这两个代码样本产生不同的输出?

class Animal { 
    public static void saySomething() { System.out.print(" Gurrr!"); 
    } 
} 
class Cow extends Animal { 
    public static void saySomething() { 
    System.out.print(" Moo!"); 
    } 
    public static void main(String [] args) { 
     Animal [] animals = {new Animal(), new Cow()}; 
     for(Animal a : animals) { 
      a.saySomething(); 
     } 
     new Cow().saySomething(); 
    } 
} 

输出是:

Gurrr! Gurrr! Moo! 

示例2:

class Animal { 
    public void saySomething() { System.out.print(" Gurrr!"); 
    } 
} 
class Cow extends Animal { 
    public void saySomething() { 
    System.out.print(" Moo!"); 
    } 
    public static void main(String [] args) { 
     Animal [] animals = {new Animal(), new Cow()}; 
     for(Animal a : animals) { 
      a.saySomething(); 
     } 
     new Cow().saySomething(); 
    } 
} 

输出:

Gurrr! Moo! Moo! 

我只是不明白为什么做saySomething非统计ic导致第二次调用saySomething调用Cow版本而不是Animal版本。我的理解是Gurrr! Moo! Moo!是两种情况下的输出。

+0

“Java语言规范说得那么简单”实际上就是这样。为什么你被允许从一个实例引用中调用一个静态方法,就像真正的奇怪一样。但是至少有一个巨大的社区维基线程:) – Affe 2010-09-09 19:55:32

回答

1

静态方法在编译时绑定到它们的类并且不能多态使用。当你在Animal上声明一个“静态”方法时,它永远与Animal类绑定,不能被覆盖。静态方法绑定到Class对象,而不是Class的实例。

常规方法在运行时绑定,因此JVM可以查看您对“saySomething”的调用并尝试确定是否传递给Animal的子类,如果是,它是否覆盖了方法saySomething()。常规方法绑定到对象的实例,而不是绑定到类本身。

这也是为什么你永远无法做到这一点:

class Animal 
{ 
    public abstract static void saySomething(); 
} 

由于“静态”的意思是“在编译时绑定”,是没有意义的东西是静态和抽象。

7

当您对动物致电saySomething()时,动物的实际类型不计数,因为saySomething()是静态的。

Animal cow = new Cow(); 
cow.saySomething(); 

相同

Animal.saySomething(); 

甲JLS例如:

当目标参考被计算,然后被丢弃,因为调用模式是静态的,所述基准是未检查是否为空:

class Test { 
    static void mountain() { 
     System.out.println("Monadnock"); 
    } 
    static Test favorite(){ 
     System.out.print("Mount "); 
     return null; 
    } 
    public static void main(String[] args) { 
     favorite().mountain(); 
    } 

}

它打印:
诺纳德诺克山
这里最喜欢返回null,但没有抛出NullPointerException。


资源:

关于同一主题:

+1

我从来没有理解为什么Java允许通过对象引用来调用静态方法。这是没有必要的,而且会导致这样的混乱。 – erickson 2010-09-09 19:53:55

+0

同意。 C++也可以让你做到这一点。我的猜测是,只是让编译器编写者容易进行这种访问。 – 2010-09-09 19:57:35

+0

那么,我试图在这里辩护:http://stackoverflow.com/questions/3610309/java-static-confusion/ – 2010-09-09 20:07:05

3

你不能覆盖与子类相同的签名,只是隐藏他们的静态方法。

对于类的方法,运行时系统调用在其上该方法被称为参考的编译时类型定义的方法。对于实例方法,运行时系统调用在其中调用该方法的参考运行时类型中定义的方法。

http://life.csu.edu.au/java-tut/java/javaOO/override.html

3

一些已知的重写 “缺陷” 一

  • 静态方法不能被重写
  • 私有方法不能被重写

这解释了输出。

1

静态方法绑定到“类”,而不是对象的“实例”。 由于您指的是“动物”并调用了静态方法saySomething()。除非你指的是母牛,否则它总是会打电话给“动物”。

相关问题